create vector4, vector4i and projection for csharp

This commit is contained in:
antonWetzel 2022-07-26 16:19:05 +02:00
parent 7686cfec64
commit 87ebfff46d
6 changed files with 2262 additions and 34 deletions

View File

@ -19,30 +19,15 @@ jobs:
fail-fast: false
matrix:
include:
# Temporarily disabled until Mono is fixed
#
# - name: Editor w Mono (target=release_debug, tools=yes, tests=yes)
# cache-name: linux-editor-mono
# target: release_debug
# tools: true
# tests: false # Disabled due freeze caused by mix Mono build and CI
# sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no
# doc-test: true
# bin: "./bin/godot.linuxbsd.opt.tools.64.mono"
# build-mono: true
# proj-conv: true
# artifact: true
# Temporary replacement:
- name: Editor w/o Mono (target=release_debug, tools=yes, tests=yes)
- name: Editor w Mono (target=release_debug, tools=yes, tests=yes)
cache-name: linux-editor-mono
target: release_debug
tools: true
tests: false # Disabled due freeze caused by mix Mono build and CI
sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no
doc-test: true
bin: "./bin/godot.linuxbsd.opt.tools.64"
build-mono: false
bin: "./bin/godot.linuxbsd.opt.tools.64.mono"
build-mono: true
proj-conv: true
artifact: true
@ -72,24 +57,12 @@ jobs:
# Skip 2GiB artifact speeding up action.
artifact: false
# Temporarily disabled:
#
# - name: Template w/ Mono (target=release, tools=no)
# cache-name: linux-template-mono
# target: release
# tools: false
# tests: false
# sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no debug_symbols=no
# build-mono: false
# artifact: true
# Temporary replacement:
- name: Template w/o Mono (target=release, tools=no)
- name: Template w/ Mono (target=release, tools=no)
cache-name: linux-template-mono
target: release
tools: false
tests: false
sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no debug_symbols=no
build-mono: false
artifact: true

View File

@ -0,0 +1,820 @@
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
using System;
using System.Runtime.InteropServices;
namespace Godot
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Projection : IEquatable<Projection>
{
/// <summary>
/// Enumerated index values for the planes.
/// </summary>
public enum Planes
{
/// <summary>
/// The projection's near plane.
/// </summary>
Near,
/// <summary>
/// The projection's far plane.
/// </summary>
Far,
/// <summary>
/// The projection's left plane.
/// </summary>
Left,
/// <summary>
/// The projection's top plane.
/// </summary>
Top,
/// <summary>
/// The projection's right plane.
/// </summary>
Right,
/// <summary>
/// The projection's bottom plane.
/// </summary>
Bottom,
}
/// <summary>
/// The projections's X column. Also accessible by using the index position <c>[0]</c>.
/// </summary>
public Vector4 x;
/// <summary>
/// The projections's Y column. Also accessible by using the index position <c>[1]</c>.
/// </summary>
public Vector4 y;
/// <summary>
/// The projections's Z column. Also accessible by using the index position <c>[2]</c>.
/// </summary>
public Vector4 z;
/// <summary>
/// The projections's W column. Also accessible by using the index position <c>[3]</c>.
/// </summary>
public Vector4 w;
/// <summary>
/// Constructs a projection from 4 vectors (matrix columns).
/// </summary>
/// <param name="x">The X column, or column index 0.</param>
/// <param name="y">The Y column, or column index 1.</param>
/// <param name="z">The Z column, or column index 2.</param>
/// <param name="w">The W column, or column index 3.</param>
public Projection(Vector4 x, Vector4 y, Vector4 z, Vector4 w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
/// <summary>
/// Constructs a new <see cref="Projection"/> from an existing <see cref="Projection"/>.
/// </summary>
/// <param name="proj">The existing <see cref="Projection"/>.</param>
public Projection(Projection proj)
{
x = proj.x;
y = proj.y;
z = proj.z;
w = proj.w;
}
/// <summary>
/// Constructs a new <see cref="Projection"/> from a <see cref="Transform3D"/>.
/// </summary>
/// <param name="transform">The <see cref="Transform3D"/>.</param>
public Projection(Transform3D transform)
{
x = new Vector4(transform.basis.Row0.x, transform.basis.Row1.x, transform.basis.Row2.x, 0);
y = new Vector4(transform.basis.Row0.y, transform.basis.Row1.y, transform.basis.Row2.y, 0);
z = new Vector4(transform.basis.Row0.z, transform.basis.Row1.z, transform.basis.Row2.z, 0);
w = new Vector4(transform.origin.x, transform.origin.y, transform.origin.z, 1);
}
/// <summary>
/// Constructs a new <see cref="Transform3D"/> from the <see cref="Projection"/>.
/// </summary>
/// <param name="proj">The <see cref="Projection"/>.</param>
public static explicit operator Transform3D(Projection proj)
{
return new Transform3D(
new Basis(
new Vector3(proj.x.x, proj.x.y, proj.x.z),
new Vector3(proj.y.x, proj.y.y, proj.y.z),
new Vector3(proj.z.x, proj.z.y, proj.z.z)
),
new Vector3(proj.w.x, proj.w.y, proj.w.z)
);
}
public static Projection CreateDepthCorrection(bool flipY)
{
return new Projection(
new Vector4(1, 0, 0, 0),
new Vector4(0, flipY ? -1 : 1, 0, 0),
new Vector4(0, 0, (real_t)0.5, 0),
new Vector4(0, 0, (real_t)0.5, 1)
);
}
public static Projection CreateFitAabb(AABB aabb)
{
Vector3 min = aabb.Position;
Vector3 max = aabb.Position + aabb.Size;
return new Projection(
new Vector4(2 / (max.x - min.x), 0, 0, 0),
new Vector4(0, 2 / (max.y - min.y), 0, 0),
new Vector4(0, 0, 2 / (max.z - min.z), 0),
new Vector4(-(max.x + min.x) / (max.x - min.x), -(max.y + min.y) / (max.y - min.y), -(max.z + min.z) / (max.z - min.z), 1)
);
}
public static Projection CreateForHmd(int eye, real_t aspect, real_t intraocularDist, real_t displayWidth, real_t displayToLens, real_t oversample, real_t zNear, real_t zFar)
{
real_t f1 = (intraocularDist * (real_t)0.5) / displayToLens;
real_t f2 = ((displayWidth - intraocularDist) * (real_t)0.5) / displayToLens;
real_t f3 = (displayWidth / (real_t)4.0) / displayToLens;
real_t add = ((f1 + f2) * (oversample - (real_t)1.0)) / (real_t)2.0;
f1 += add;
f2 += add;
f3 *= oversample;
f3 /= aspect;
switch (eye)
{
case 1:
return CreateFrustum(-f2 * zNear, f1 * zNear, -f3 * zNear, f3 * zNear, zNear, zFar);
case 2:
return CreateFrustum(-f1 * zNear, f2 * zNear, -f3 * zNear, f3 * zNear, zNear, zFar);
default:
return Zero;
}
}
public static Projection CreateFrustum(real_t left, real_t right, real_t bottom, real_t top, real_t near, real_t far)
{
if (right <= left)
{
throw new ArgumentException("right is less or equal to left.");
}
if (top <= bottom)
{
throw new ArgumentException("top is less or equal to bottom.");
}
if (far <= near)
{
throw new ArgumentException("far is less or equal to near.");
}
real_t x = 2 * near / (right - left);
real_t y = 2 * near / (top - bottom);
real_t a = (right + left) / (right - left);
real_t b = (top + bottom) / (top - bottom);
real_t c = -(far + near) / (far - near);
real_t d = -2 * far * near / (far - near);
return new Projection(
new Vector4(x, 0, 0, 0),
new Vector4(0, y, 0, 0),
new Vector4(a, b, c, -1),
new Vector4(0, 0, d, 0)
);
}
public static Projection CreateFrustumAspect(real_t size, real_t aspect, Vector2 offset, real_t near, real_t far, bool flipFov)
{
if (!flipFov)
{
size *= aspect;
}
return CreateFrustum(-size / 2 + offset.x, +size / 2 + offset.x, -size / aspect / 2 + offset.y, +size / aspect / 2 + offset.y, near, far);
}
public static Projection CreateLightAtlasRect(Rect2 rect)
{
return new Projection(
new Vector4(rect.Size.x, 0, 0, 0),
new Vector4(0, rect.Size.y, 0, 0),
new Vector4(0, 0, 1, 0),
new Vector4(rect.Position.x, rect.Position.y, 0, 1)
);
}
public static Projection CreateOrthogonal(real_t left, real_t right, real_t bottom, real_t top, real_t zNear, real_t zFar)
{
Projection proj = Projection.Identity;
proj.x.x = (real_t)2.0 / (right - left);
proj.w.x = -((right + left) / (right - left));
proj.y.y = (real_t)2.0 / (top - bottom);
proj.w.y = -((top + bottom) / (top - bottom));
proj.z.z = (real_t)(-2.0) / (zFar - zNear);
proj.w.z = -((zFar + zNear) / (zFar - zNear));
proj.w.w = (real_t)1.0;
return proj;
}
public static Projection CreateOrthogonalAspect(real_t size, real_t aspect, real_t zNear, real_t zFar, bool flipFov)
{
if (!flipFov)
{
size *= aspect;
}
return CreateOrthogonal(-size / 2, +size / 2, -size / aspect / 2, +size / aspect / 2, zNear, zFar);
}
public static Projection CreatePerspective(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov)
{
if (flipFov)
{
fovyDegrees = GetFovy(fovyDegrees, (real_t)1.0 / aspect);
}
real_t radians = Mathf.Deg2Rad(fovyDegrees / (real_t)2.0);
real_t deltaZ = zFar - zNear;
real_t sine = Mathf.Sin(radians);
if ((deltaZ == 0) || (sine == 0) || (aspect == 0))
{
return Zero;
}
real_t cotangent = Mathf.Cos(radians) / sine;
Projection proj = Projection.Identity;
proj.x.x = cotangent / aspect;
proj.y.y = cotangent;
proj.z.z = -(zFar + zNear) / deltaZ;
proj.z.w = -1;
proj.w.z = -2 * zNear * zFar / deltaZ;
proj.w.w = 0;
return proj;
}
public static Projection CreatePerspectiveHmd(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov, int eye, real_t intraocularDist, real_t convergenceDist)
{
if (flipFov)
{
fovyDegrees = GetFovy(fovyDegrees, (real_t)1.0 / aspect);
}
real_t ymax = zNear * Mathf.Tan(Mathf.Deg2Rad(fovyDegrees / (real_t)2.0));
real_t xmax = ymax * aspect;
real_t frustumshift = (intraocularDist / (real_t)2.0) * zNear / convergenceDist;
real_t left;
real_t right;
real_t modeltranslation;
switch (eye)
{
case 1:
left = -xmax + frustumshift;
right = xmax + frustumshift;
modeltranslation = intraocularDist / (real_t)2.0;
break;
case 2:
left = -xmax - frustumshift;
right = xmax - frustumshift;
modeltranslation = -intraocularDist / (real_t)2.0;
break;
default:
left = -xmax;
right = xmax;
modeltranslation = (real_t)0.0;
break;
}
Projection proj = CreateFrustum(left, right, -ymax, ymax, zNear, zFar);
Projection cm = Projection.Identity;
cm.w.x = modeltranslation;
return proj * cm;
}
public real_t Determinant()
{
return x.w * y.z * z.y * w.x - x.z * y.w * z.y * w.x -
x.w * y.y * z.z * w.x + x.y * y.w * z.z * w.x +
x.z * y.y * z.w * w.x - x.y * y.z * z.w * w.x -
x.w * y.z * z.x * w.y + x.z * y.w * z.x * w.y +
x.w * y.x * z.z * w.y - x.x * y.w * z.z * w.y -
x.z * y.x * z.w * w.y + x.x * y.z * z.w * w.y +
x.w * y.y * z.x * w.z - x.y * y.w * z.x * w.z -
x.w * y.x * z.y * w.z + x.x * y.w * z.y * w.z +
x.y * y.x * z.w * w.z - x.x * y.y * z.w * w.z -
x.z * y.y * z.x * w.w + x.y * y.z * z.x * w.w +
x.z * y.x * z.y * w.w - x.x * y.z * z.y * w.w -
x.y * y.x * z.z * w.w + x.x * y.y * z.z * w.w;
}
public real_t GetAspect()
{
Vector2 vpHe = GetViewportHalfExtents();
return vpHe.x / vpHe.y;
}
public real_t GetFov()
{
Plane rightPlane = new Plane(x.w - x.x, y.w - y.x, z.w - z.x, -w.w + w.x).Normalized();
if (z.x == 0 && z.y == 0)
{
return Mathf.Rad2Deg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.x))) * (real_t)2.0;
}
else
{
Plane leftPlane = new Plane(x.w + x.x, y.w + y.x, z.w + z.x, w.w + w.x).Normalized();
return Mathf.Rad2Deg(Mathf.Acos(Mathf.Abs(leftPlane.Normal.x))) + Mathf.Rad2Deg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.x)));
}
}
public static real_t GetFovy(real_t fovx, real_t aspect)
{
return Mathf.Rad2Deg(Mathf.Atan(aspect * Mathf.Tan(Mathf.Deg2Rad(fovx) * (real_t)0.5)) * (real_t)2.0);
}
public real_t GetLodMultiplier()
{
if (IsOrthogonal())
{
return GetViewportHalfExtents().x;
}
else
{
real_t zn = GetZNear();
real_t width = GetViewportHalfExtents().x * (real_t)2.0;
return (real_t)1.0 / (zn / width);
}
}
public int GetPixelsPerMeter(int forPixelWidth)
{
Vector3 result = Xform(new Vector3(1, 0, -1));
return (int)((result.x * (real_t)0.5 + (real_t)0.5) * forPixelWidth);
}
public Plane GetProjectionPlane(Planes plane)
{
Plane newPlane = plane switch
{
Planes.Near => new Plane(x.w + x.z, y.w + y.z, z.w + z.z, w.w + w.z),
Planes.Far => new Plane(x.w - x.z, y.w - y.z, z.w - z.z, w.w - w.z),
Planes.Left => new Plane(x.w + x.x, y.w + y.x, z.w + z.x, w.w + w.x),
Planes.Top => new Plane(x.w - x.y, y.w - y.y, z.w - z.y, w.w - w.y),
Planes.Right => new Plane(x.w - x.x, y.w - y.x, z.w - z.x, w.w - w.x),
Planes.Bottom => new Plane(x.w + x.y, y.w + y.y, z.w + z.y, w.w + w.y),
_ => new Plane(),
};
newPlane.Normal = -newPlane.Normal;
return newPlane.Normalized();
}
public Vector2 GetFarPlaneHalfExtents()
{
var res = GetProjectionPlane(Planes.Far).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top));
return new Vector2(res.Value.x, res.Value.y);
}
public Vector2 GetViewportHalfExtents()
{
var res = GetProjectionPlane(Planes.Near).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top));
return new Vector2(res.Value.x, res.Value.y);
}
public real_t GetZFar()
{
return GetProjectionPlane(Planes.Far).D;
}
public real_t GetZNear()
{
return -GetProjectionPlane(Planes.Near).D;
}
public Projection FlippedY()
{
Projection proj = this;
proj.y = -proj.y;
return proj;
}
public Projection PerspectiveZNearAdjusted(real_t newZNear)
{
Projection proj = this;
real_t zFar = GetZFar();
real_t zNear = newZNear;
real_t deltaZ = zFar - zNear;
proj.z.z = -(zFar + zNear) / deltaZ;
proj.w.z = -2 * zNear * zFar / deltaZ;
return proj;
}
public Projection JitterOffseted(Vector2 offset)
{
Projection proj = this;
proj.w.x += offset.x;
proj.w.y += offset.y;
return proj;
}
public Projection Inverse()
{
Projection proj = this;
int i, j, k;
int[] pvt_i = new int[4];
int[] pvt_j = new int[4]; /* Locations of pivot matrix */
real_t pvt_val; /* Value of current pivot element */
real_t hold; /* Temporary storage */
real_t determinant = 1.0f;
for (k = 0; k < 4; k++)
{
/* Locate k'th pivot element */
pvt_val = proj[k][k]; /* Initialize for search */
pvt_i[k] = k;
pvt_j[k] = k;
for (i = k; i < 4; i++)
{
for (j = k; j < 4; j++)
{
if (Mathf.Abs(proj[i][j]) > Mathf.Abs(pvt_val))
{
pvt_i[k] = i;
pvt_j[k] = j;
pvt_val = proj[i][j];
}
}
}
/* Product of pivots, gives determinant when finished */
determinant *= pvt_val;
if (Mathf.IsZeroApprox(determinant))
{
return Zero;
}
/* "Interchange" rows (with sign change stuff) */
i = pvt_i[k];
if (i != k)
{ /* If rows are different */
for (j = 0; j < 4; j++)
{
hold = -proj[k][j];
proj[k, j] = proj[i][j];
proj[i, j] = hold;
}
}
/* "Interchange" columns */
j = pvt_j[k];
if (j != k)
{ /* If columns are different */
for (i = 0; i < 4; i++)
{
hold = -proj[i][k];
proj[i, k] = proj[i][j];
proj[i, j] = hold;
}
}
/* Divide column by minus pivot value */
for (i = 0; i < 4; i++)
{
if (i != k)
{
proj[i, k] /= (-pvt_val);
}
}
/* Reduce the matrix */
for (i = 0; i < 4; i++)
{
hold = proj[i][k];
for (j = 0; j < 4; j++)
{
if (i != k && j != k)
{
proj[i, j] += hold * proj[k][j];
}
}
}
/* Divide row by pivot */
for (j = 0; j < 4; j++)
{
if (j != k)
{
proj[k, j] /= pvt_val;
}
}
/* Replace pivot by reciprocal (at last we can touch it). */
proj[k, k] = (real_t)1.0 / pvt_val;
}
/* That was most of the work, one final pass of row/column interchange */
/* to finish */
for (k = 4 - 2; k >= 0; k--)
{ /* Don't need to work with 1 by 1 corner*/
i = pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */
if (i != k)
{ /* If rows are different */
for (j = 0; j < 4; j++)
{
hold = proj[k][j];
proj[k, j] = -proj[i][j];
proj[i, j] = hold;
}
}
j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */
if (j != k)
{ /* If columns are different */
for (i = 0; i < 4; i++)
{
hold = proj[i][k];
proj[i, k] = -proj[i][j];
proj[i, j] = hold;
}
}
}
return proj;
}
public bool IsOrthogonal()
{
return w.w == (real_t)1.0;
}
/// <summary>
/// Composes these two projections by multiplying them
/// together. This has the effect of applying the right
/// and then the left projection.
/// </summary>
/// <param name="left">The parent transform.</param>
/// <param name="right">The child transform.</param>
/// <returns>The composed projection.</returns>
public static Projection operator *(Projection left, Projection right)
{
return new Projection(
new Vector4(
left.x.x * right.x.x + left.y.x * right.x.y + left.z.x * right.x.z + left.w.x * right.x.w,
left.x.y * right.x.x + left.y.y * right.x.y + left.z.y * right.x.z + left.w.y * right.x.w,
left.x.z * right.x.x + left.y.z * right.x.y + left.z.z * right.x.z + left.w.z * right.x.w,
left.x.w * right.x.x + left.y.w * right.x.y + left.z.w * right.x.z + left.w.w * right.x.w
), new Vector4(
left.x.x * right.y.x + left.y.x * right.y.y + left.z.x * right.y.z + left.w.x * right.y.w,
left.x.y * right.y.x + left.y.y * right.y.y + left.z.y * right.y.z + left.w.y * right.y.w,
left.x.z * right.y.x + left.y.z * right.y.y + left.z.z * right.y.z + left.w.z * right.y.w,
left.x.w * right.y.x + left.y.w * right.y.y + left.z.w * right.y.z + left.w.w * right.y.w
), new Vector4(
left.x.x * right.z.x + left.y.x * right.z.y + left.z.x * right.z.z + left.w.x * right.z.w,
left.x.y * right.z.x + left.y.y * right.z.y + left.z.y * right.z.z + left.w.y * right.z.w,
left.x.z * right.z.x + left.y.z * right.z.y + left.z.z * right.z.z + left.w.z * right.z.w,
left.x.w * right.z.x + left.y.w * right.z.y + left.z.w * right.z.z + left.w.w * right.z.w
), new Vector4(
left.x.x * right.w.x + left.y.x * right.w.y + left.z.x * right.w.z + left.w.x * right.w.w,
left.x.y * right.w.x + left.y.y * right.w.y + left.z.y * right.w.z + left.w.y * right.w.w,
left.x.z * right.w.x + left.y.z * right.w.y + left.z.z * right.w.z + left.w.z * right.w.w,
left.x.w * right.w.x + left.y.w * right.w.y + left.z.w * right.w.z + left.w.w * right.w.w
)
);
}
/// <summary>
/// Returns a vector transformed (multiplied) by this projection.
/// </summary>
/// <param name="proj">The projection to apply.</param>
/// <param name="v">A vector to transform.</param>
/// <returns>The transformed vector.</returns>
public static Vector4 operator *(Projection proj, Vector4 v)
{
return new Vector4(
proj.x.x * v.x + proj.y.x * v.y + proj.z.x * v.z + proj.w.x * v.w,
proj.x.y * v.x + proj.y.y * v.y + proj.z.y * v.z + proj.w.y * v.w,
proj.x.z * v.x + proj.y.z * v.y + proj.z.z * v.z + proj.w.z * v.w,
proj.x.w * v.x + proj.y.w * v.y + proj.z.w * v.z + proj.w.w * v.w
);
}
/// <summary>
/// Returns <see langword="true"/> if the projections are exactly equal.
/// </summary>
/// <param name="left">The left projection.</param>
/// <param name="right">The right projection.</param>
/// <returns>Whether or not the projections are exactly equal.</returns>
public static bool operator ==(Projection left, Projection right)
{
return left.Equals(right);
}
/// <summary>
/// Returns <see langword="true"/> if the projections are not exactly equal.
/// </summary>
/// <param name="left">The left projection.</param>
/// <param name="right">The right projection.</param>
/// <returns>Whether or not the projections are not exactly equal.</returns>
public static bool operator !=(Projection left, Projection right)
{
return !left.Equals(right);
}
/// <summary>
/// Access whole columns in the form of <see cref="Vector4"/>.
/// </summary>
/// <param name="column">Which column vector.</param>
public Vector4 this[int column]
{
get
{
switch (column)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
case 3:
return w;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (column)
{
case 0:
x = value;
return;
case 1:
y = value;
return;
case 2:
z = value;
return;
case 3:
w = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
/// <summary>
/// Access single values.
/// </summary>
/// <param name="column">Which column vector.</param>
/// <param name="row">Which row of the column.</param>
public real_t this[int column, int row]
{
get
{
switch (column)
{
case 0:
return x[row];
case 1:
return y[row];
case 2:
return z[row];
case 3:
return w[row];
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (column)
{
case 0:
x[row] = value;
return;
case 1:
y[row] = value;
return;
case 2:
z[row] = value;
return;
case 3:
w[row] = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
/// <summary>
/// Returns a vector transformed (multiplied) by this projection.
/// </summary>
/// <param name="v">A vector to transform.</param>
/// <returns>The transformed vector.</returns>
private Vector3 Xform(Vector3 v)
{
Vector3 ret = new Vector3(
x.x * v.x + y.x * v.y + z.x * v.z + w.x,
x.y * v.x + y.y * v.y + z.y * v.z + w.y,
x.z * v.x + y.z * v.y + z.z * v.z + w.z
);
return ret / (x.w * v.x + y.w * v.y + z.w * v.z + w.w);
}
// Constants
private static readonly Projection _zero = new Projection(
new Vector4(0, 0, 0, 0),
new Vector4(0, 0, 0, 0),
new Vector4(0, 0, 0, 0),
new Vector4(0, 0, 0, 0)
);
private static readonly Projection _identity = new Projection(
new Vector4(1, 0, 0, 0),
new Vector4(0, 1, 0, 0),
new Vector4(0, 0, 1, 0),
new Vector4(0, 0, 0, 1)
);
/// <summary>
/// Zero projection, a projection with all components set to <c>0</c>.
/// </summary>
/// <value>Equivalent to <c>new Projection(Vector4.Zero, Vector4.Zero, Vector4.Zero, Vector4.Zero)</c>.</value>
public static Projection Zero { get { return _zero; } }
/// <summary>
/// The identity projection, with no distortion applied.
/// This is used as a replacement for <c>Projection()</c> in GDScript.
/// Do not use <c>new Projection()</c> with no arguments in C#, because it sets all values to zero.
/// </summary>
/// <value>Equivalent to <c>new Projection(new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1))</c>.</value>
public static Projection Identity { get { return _identity; } }
/// <summary>
/// Serves as the hash function for <see cref="Projection"/>.
/// </summary>
/// <returns>A hash code for this projection.</returns>
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
}
/// <summary>
/// Converts this <see cref="Projection"/> to a string.
/// </summary>
/// <returns>A string representation of this projection.</returns>
public override string ToString()
{
return $"{x.x}, {x.y}, {x.z}, {x.w}\n{y.x}, {y.y}, {y.z}, {y.w}\n{z.x}, {z.y}, {z.z}, {z.w}\n{w.x}, {w.y}, {w.z}, {w.w}\n";
}
/// <summary>
/// Converts this <see cref="Projection"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this projection.</returns>
public string ToString(string format)
{
return $"{x.x.ToString(format)}, {x.y.ToString(format)}, {x.z.ToString(format)}, {x.w.ToString(format)}\n" +
$"{y.x.ToString(format)}, {y.y.ToString(format)}, {y.z.ToString(format)}, {y.w.ToString(format)}\n" +
$"{z.x.ToString(format)}, {z.y.ToString(format)}, {z.z.ToString(format)}, {z.w.ToString(format)}\n" +
$"{w.x.ToString(format)}, {w.y.ToString(format)}, {w.z.ToString(format)}, {w.w.ToString(format)}\n";
}
/// <summary>
/// Returns <see langword="true"/> if the projection is exactly equal
/// to the given object (<see paramref="obj"/>).
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Projection)
{
return Equals((Projection)obj);
}
return false;
}
/// <summary>
/// Returns <see langword="true"/> if the projections are exactly equal.
/// </summary>
/// <param name="other">The other projection.</param>
/// <returns>Whether or not the projections are exactly equal.</returns>
public bool Equals(Projection other)
{
return x == other.x && y == other.y && z == other.z && w == other.w;
}
}
}

View File

@ -0,0 +1,730 @@
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
using System;
using System.Runtime.InteropServices;
namespace Godot
{
/// <summary>
/// 4-element structure that can be used to represent positions in 4D space or any other pair of numeric values.
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Vector4 : IEquatable<Vector4>
{
/// <summary>
/// Enumerated index values for the axes.
/// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
/// </summary>
public enum Axis
{
/// <summary>
/// The vector's X axis.
/// </summary>
X = 0,
/// <summary>
/// The vector's Y axis.
/// </summary>
Y,
/// <summary>
/// The vector's Z axis.
/// </summary>
Z,
/// <summary>
/// The vector's W axis.
/// </summary>
W
}
/// <summary>
/// The vector's X component. Also accessible by using the index position <c>[0]</c>.
/// </summary>
public real_t x;
/// <summary>
/// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
/// </summary>
public real_t y;
/// <summary>
/// The vector's Z component. Also accessible by using the index position <c>[2]</c>.
/// </summary>
public real_t z;
/// <summary>
/// The vector's W component. Also accessible by using the index position <c>[3]</c>.
/// </summary>
public real_t w;
/// <summary>
/// Access vector components using their index.
/// </summary>
/// <exception cref="IndexOutOfRangeException">
/// Thrown when the given the <paramref name="index"/> is not 0, 1, 2 or 3.
/// </exception>
/// <value>
/// <c>[0]</c> is equivalent to <see cref="x"/>,
/// <c>[1]</c> is equivalent to <see cref="y"/>,
/// <c>[2]</c> is equivalent to <see cref="z"/>.
/// <c>[3]</c> is equivalent to <see cref="w"/>.
/// </value>
public real_t this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
case 3:
return w;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
return;
case 1:
y = value;
return;
case 2:
z = value;
return;
case 3:
w = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
/// <summary>
/// Helper method for deconstruction into a tuple.
/// </summary>
public void Deconstruct(out real_t x, out real_t y, out real_t z, out real_t w)
{
x = this.x;
y = this.y;
z = this.z;
w = this.w;
}
internal void Normalize()
{
real_t lengthsq = LengthSquared();
if (lengthsq == 0)
{
x = y = z = w = 0f;
}
else
{
real_t length = Mathf.Sqrt(lengthsq);
x /= length;
y /= length;
z /= length;
w /= length;
}
}
/// <summary>
/// Returns a new vector with all components in absolute values (i.e. positive).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns>
public Vector4 Abs()
{
return new Vector4(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z), Mathf.Abs(w));
}
/// <summary>
/// Returns a new vector with all components rounded up (towards positive infinity).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
public Vector4 Ceil()
{
return new Vector4(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z), Mathf.Ceil(w));
}
/// <summary>
/// Returns a new vector with all components clamped between the
/// components of <paramref name="min"/> and <paramref name="max"/> using
/// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>.
/// </summary>
/// <param name="min">The vector with minimum allowed values.</param>
/// <param name="max">The vector with maximum allowed values.</param>
/// <returns>The vector with all components clamped.</returns>
public Vector4 Clamp(Vector4 min, Vector4 max)
{
return new Vector4
(
Mathf.Clamp(x, min.x, max.x),
Mathf.Clamp(y, min.y, max.y),
Mathf.Clamp(z, min.z, max.z),
Mathf.Clamp(w, min.w, max.w)
);
}
/// <summary>
/// Returns a new vector with all components rounded down (towards negative infinity).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
public Vector4 Floor()
{
return new Vector4(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z), Mathf.Floor(w));
}
/// <summary>
/// Returns the dot product of this vector and <paramref name="with"/>.
/// </summary>
/// <param name="with">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
public real_t Dot(Vector4 with)
{
return (x * with.x) + (y * with.y) + (z * with.z) + (w + with.w);
}
/// <summary>
/// Returns the inverse of this vector. This is the same as <c>new Vector4(1 / v.x, 1 / v.y, 1 / v.z, 1 / v.w)</c>.
/// </summary>
/// <returns>The inverse of this vector.</returns>
public Vector4 Inverse()
{
return new Vector4(1 / x, 1 / y, 1 / z, 1 / w);
}
/// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
public bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
/// <summary>
/// Returns the length (magnitude) of this vector.
/// </summary>
/// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
public real_t Length()
{
real_t x2 = x * x;
real_t y2 = y * y;
real_t z2 = z * z;
real_t w2 = w * w;
return Mathf.Sqrt(x2 + y2 + z2 + w2);
}
/// <summary>
/// Returns the squared length (squared magnitude) of this vector.
/// This method runs faster than <see cref="Length"/>, so prefer it if
/// you need to compare vectors or need the squared length for some formula.
/// </summary>
/// <returns>The squared length of this vector.</returns>
public real_t LengthSquared()
{
real_t x2 = x * x;
real_t y2 = y * y;
real_t z2 = z * z;
real_t w2 = w * w;
return x2 + y2 + z2 + w2;
}
/// <summary>
/// Returns the result of the linear interpolation between
/// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
public Vector4 Lerp(Vector4 to, real_t weight)
{
return new Vector4
(
Mathf.Lerp(x, to.x, weight),
Mathf.Lerp(y, to.y, weight),
Mathf.Lerp(z, to.z, weight),
Mathf.Lerp(w, to.w, weight)
);
}
/// <summary>
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
/// <returns>The index of the highest axis.</returns>
public Axis MaxAxisIndex()
{
int max_index = 0;
real_t max_value = x;
for (int i = 1; i < 4; i++)
{
if (this[i] > max_value)
{
max_index = i;
max_value = this[i];
}
}
return (Axis)max_index;
}
/// <summary>
/// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.W"/>.
/// </summary>
/// <returns>The index of the lowest axis.</returns>
public Axis MinAxisIndex()
{
int min_index = 0;
real_t min_value = x;
for (int i = 1; i < 4; i++)
{
if (this[i] <= min_value)
{
min_index = i;
min_value = this[i];
}
}
return (Axis)min_index;
}
/// <summary>
/// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>.
/// </summary>
/// <returns>A normalized version of the vector.</returns>
public Vector4 Normalized()
{
Vector4 v = this;
v.Normalize();
return v;
}
/// <summary>
/// Returns this vector with all components rounded to the nearest integer,
/// with halfway cases rounded towards the nearest multiple of two.
/// </summary>
/// <returns>The rounded vector.</returns>
public Vector4 Round()
{
return new Vector4(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z), Mathf.Round(w));
}
/// <summary>
/// Returns a vector with each component set to one or negative one, depending
/// on the signs of this vector's components, or zero if the component is zero,
/// by calling <see cref="Mathf.Sign(real_t)"/> on each component.
/// </summary>
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
public Vector4 Sign()
{
Vector4 v;
v.x = Mathf.Sign(x);
v.y = Mathf.Sign(y);
v.z = Mathf.Sign(z);
v.w = Mathf.Sign(w);
return v;
}
// Constants
private static readonly Vector4 _zero = new Vector4(0, 0, 0, 0);
private static readonly Vector4 _one = new Vector4(1, 1, 1, 1);
private static readonly Vector4 _inf = new Vector4(Mathf.Inf, Mathf.Inf, Mathf.Inf, Mathf.Inf);
/// <summary>
/// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
/// <value>Equivalent to <c>new Vector4(0, 0, 0, 0)</c>.</value>
public static Vector4 Zero { get { return _zero; } }
/// <summary>
/// One vector, a vector with all components set to <c>1</c>.
/// </summary>
/// <value>Equivalent to <c>new Vector4(1, 1, 1, 1)</c>.</value>
public static Vector4 One { get { return _one; } }
/// <summary>
/// Infinity vector, a vector with all components set to <see cref="Mathf.Inf"/>.
/// </summary>
/// <value>Equivalent to <c>new Vector4(Mathf.Inf, Mathf.Inf, Mathf.Inf, Mathf.Inf)</c>.</value>
public static Vector4 Inf { get { return _inf; } }
/// <summary>
/// Constructs a new <see cref="Vector4"/> with the given components.
/// </summary>
/// <param name="x">The vector's X component.</param>
/// <param name="y">The vector's Y component.</param>
/// <param name="z">The vector's Z component.</param>
/// <param name="w">The vector's W component.</param>
public Vector4(real_t x, real_t y, real_t z, real_t w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
/// <summary>
/// Constructs a new <see cref="Vector4"/> from an existing <see cref="Vector4"/>.
/// </summary>
/// <param name="v">The existing <see cref="Vector4"/>.</param>
public Vector4(Vector4 v)
{
x = v.x;
y = v.y;
z = v.z;
w = v.w;
}
/// <summary>
/// Adds each component of the <see cref="Vector4"/>
/// with the components of the given <see cref="Vector4"/>.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>The added vector.</returns>
public static Vector4 operator +(Vector4 left, Vector4 right)
{
left.x += right.x;
left.y += right.y;
left.z += right.z;
left.w += right.w;
return left;
}
/// <summary>
/// Subtracts each component of the <see cref="Vector4"/>
/// by the components of the given <see cref="Vector4"/>.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>The subtracted vector.</returns>
public static Vector4 operator -(Vector4 left, Vector4 right)
{
left.x -= right.x;
left.y -= right.y;
left.z -= right.z;
left.w -= right.w;
return left;
}
/// <summary>
/// Returns the negative value of the <see cref="Vector4"/>.
/// This is the same as writing <c>new Vector4(-v.x, -v.y, -v.z, -v.w)</c>.
/// This operation flips the direction of the vector while
/// keeping the same magnitude.
/// With floats, the number zero can be either positive or negative.
/// </summary>
/// <param name="vec">The vector to negate/flip.</param>
/// <returns>The negated/flipped vector.</returns>
public static Vector4 operator -(Vector4 vec)
{
vec.x = -vec.x;
vec.y = -vec.y;
vec.z = -vec.z;
vec.w = -vec.w;
return vec;
}
/// <summary>
/// Multiplies each component of the <see cref="Vector4"/>
/// by the given <see cref="real_t"/>.
/// </summary>
/// <param name="vec">The vector to multiply.</param>
/// <param name="scale">The scale to multiply by.</param>
/// <returns>The multiplied vector.</returns>
public static Vector4 operator *(Vector4 vec, real_t scale)
{
vec.x *= scale;
vec.y *= scale;
vec.z *= scale;
vec.w *= scale;
return vec;
}
/// <summary>
/// Multiplies each component of the <see cref="Vector4"/>
/// by the given <see cref="real_t"/>.
/// </summary>
/// <param name="scale">The scale to multiply by.</param>
/// <param name="vec">The vector to multiply.</param>
/// <returns>The multiplied vector.</returns>
public static Vector4 operator *(real_t scale, Vector4 vec)
{
vec.x *= scale;
vec.y *= scale;
vec.z *= scale;
vec.w *= scale;
return vec;
}
/// <summary>
/// Multiplies each component of the <see cref="Vector4"/>
/// by the components of the given <see cref="Vector4"/>.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>The multiplied vector.</returns>
public static Vector4 operator *(Vector4 left, Vector4 right)
{
left.x *= right.x;
left.y *= right.y;
left.z *= right.z;
left.w *= right.w;
return left;
}
/// <summary>
/// Divides each component of the <see cref="Vector4"/>
/// by the given <see cref="real_t"/>.
/// </summary>
/// <param name="vec">The dividend vector.</param>
/// <param name="divisor">The divisor value.</param>
/// <returns>The divided vector.</returns>
public static Vector4 operator /(Vector4 vec, real_t divisor)
{
vec.x /= divisor;
vec.y /= divisor;
vec.z /= divisor;
vec.w /= divisor;
return vec;
}
/// <summary>
/// Divides each component of the <see cref="Vector4"/>
/// by the components of the given <see cref="Vector4"/>.
/// </summary>
/// <param name="vec">The dividend vector.</param>
/// <param name="divisorv">The divisor vector.</param>
/// <returns>The divided vector.</returns>
public static Vector4 operator /(Vector4 vec, Vector4 divisorv)
{
vec.x /= divisorv.x;
vec.y /= divisorv.y;
vec.z /= divisorv.z;
vec.w /= divisorv.w;
return vec;
}
/// <summary>
/// Returns <see langword="true"/> if the vectors are exactly equal.
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the vectors are exactly equal.</returns>
public static bool operator ==(Vector4 left, Vector4 right)
{
return left.Equals(right);
}
/// <summary>
/// Returns <see langword="true"/> if the vectors are not equal.
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the vectors are not equal.</returns>
public static bool operator !=(Vector4 left, Vector4 right)
{
return !left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Vector4"/> vectors by first checking if
/// the X value of the <paramref name="left"/> vector is less than
/// the X value of the <paramref name="right"/> vector.
/// If the X values are exactly equal, then it repeats this check
/// with the Y, Z and finally W values of the two vectors.
/// This operator is useful for sorting vectors.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the left is less than the right.</returns>
public static bool operator <(Vector4 left, Vector4 right)
{
if (left.x == right.x)
{
if (left.y == right.y)
{
if (left.z == right.z)
{
return left.w < right.w;
}
return left.z < right.z;
}
return left.y < right.y;
}
return left.x < right.x;
}
/// <summary>
/// Compares two <see cref="Vector4"/> vectors by first checking if
/// the X value of the <paramref name="left"/> vector is greater than
/// the X value of the <paramref name="right"/> vector.
/// If the X values are exactly equal, then it repeats this check
/// with the Y, Z and finally W values of the two vectors.
/// This operator is useful for sorting vectors.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the left is greater than the right.</returns>
public static bool operator >(Vector4 left, Vector4 right)
{
if (left.x == right.x)
{
if (left.y == right.y)
{
if (left.z == right.z)
{
return left.w > right.w;
}
return left.z > right.z;
}
return left.y > right.y;
}
return left.x > right.x;
}
/// <summary>
/// Compares two <see cref="Vector4"/> vectors by first checking if
/// the X value of the <paramref name="left"/> vector is less than
/// or equal to the X value of the <paramref name="right"/> vector.
/// If the X values are exactly equal, then it repeats this check
/// with the Y, Z and finally W values of the two vectors.
/// This operator is useful for sorting vectors.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the left is less than or equal to the right.</returns>
public static bool operator <=(Vector4 left, Vector4 right)
{
if (left.x == right.x)
{
if (left.y == right.y)
{
if (left.z == right.z)
{
return left.w <= right.w;
}
return left.z < right.z;
}
return left.y < right.y;
}
return left.x < right.x;
}
/// <summary>
/// Compares two <see cref="Vector4"/> vectors by first checking if
/// the X value of the <paramref name="left"/> vector is greater than
/// or equal to the X value of the <paramref name="right"/> vector.
/// If the X values are exactly equal, then it repeats this check
/// with the Y, Z and finally W values of the two vectors.
/// This operator is useful for sorting vectors.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the left is greater than or equal to the right.</returns>
public static bool operator >=(Vector4 left, Vector4 right)
{
if (left.x == right.x)
{
if (left.y == right.y)
{
if (left.z == right.z)
{
return left.w >= right.w;
}
return left.z > right.z;
}
return left.y > right.y;
}
return left.x > right.x;
}
/// <summary>
/// Returns <see langword="true"/> if the vector is exactly equal
/// to the given object (<see paramref="obj"/>).
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Vector4)
{
return Equals((Vector4)obj);
}
return false;
}
/// <summary>
/// Returns <see langword="true"/> if the vectors are exactly equal.
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
/// <param name="other">The other vector.</param>
/// <returns>Whether or not the vectors are exactly equal.</returns>
public bool Equals(Vector4 other)
{
return x == other.x && y == other.y && z == other.z && w == other.w;
}
/// <summary>
/// Returns <see langword="true"/> if this vector and <paramref name="other"/> are approximately equal,
/// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
/// </summary>
/// <param name="other">The other vector to compare.</param>
/// <returns>Whether or not the vectors are approximately equal.</returns>
public bool IsEqualApprox(Vector4 other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
}
/// <summary>
/// Serves as the hash function for <see cref="Vector4"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
}
/// <summary>
/// Converts this <see cref="Vector4"/> to a string.
/// </summary>
/// <returns>A string representation of this vector.</returns>
public override string ToString()
{
return $"({x}, {y}, {z}, {w})";
}
/// <summary>
/// Converts this <see cref="Vector4"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this vector.</returns>
public string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})";
}
}
}

View File

@ -0,0 +1,702 @@
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
using System;
using System.Runtime.InteropServices;
namespace Godot
{
/// <summary>
/// 4-element structure that can be used to represent 4D grid coordinates or sets of integers.
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Vector4i : IEquatable<Vector4i>
{
/// <summary>
/// Enumerated index values for the axes.
/// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
/// </summary>
public enum Axis
{
/// <summary>
/// The vector's X axis.
/// </summary>
X = 0,
/// <summary>
/// The vector's Y axis.
/// </summary>
Y,
/// <summary>
/// The vector's Z axis.
/// </summary>
Z,
/// <summary>
/// The vector's W axis.
/// </summary>
W
}
/// <summary>
/// The vector's X component. Also accessible by using the index position <c>[0]</c>.
/// </summary>
public int x;
/// <summary>
/// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
/// </summary>
public int y;
/// <summary>
/// The vector's Z component. Also accessible by using the index position <c>[2]</c>.
/// </summary>
public int z;
/// <summary>
/// The vector's W component. Also accessible by using the index position <c>[3]</c>.
/// </summary>
public int w;
/// <summary>
/// Access vector components using their <paramref name="index"/>.
/// </summary>
/// <exception cref="IndexOutOfRangeException">
/// Thrown when the given the <paramref name="index"/> is not 0, 1, 2 or 3.
/// </exception>
/// <value>
/// <c>[0]</c> is equivalent to <see cref="x"/>,
/// <c>[1]</c> is equivalent to <see cref="y"/>,
/// <c>[2]</c> is equivalent to <see cref="z"/>.
/// <c>[3]</c> is equivalent to <see cref="w"/>.
/// </value>
public int this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
case 3:
return w;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
return;
case 1:
y = value;
return;
case 2:
z = value;
return;
case 3:
w = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
/// <summary>
/// Helper method for deconstruction into a tuple.
/// </summary>
public void Deconstruct(out int x, out int y, out int z, out int w)
{
x = this.x;
y = this.y;
z = this.z;
w = this.w;
}
/// <summary>
/// Returns a new vector with all components in absolute values (i.e. positive).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
public Vector4i Abs()
{
return new Vector4i(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z), Mathf.Abs(w));
}
/// <summary>
/// Returns a new vector with all components clamped between the
/// components of <paramref name="min"/> and <paramref name="max"/> using
/// <see cref="Mathf.Clamp(int, int, int)"/>.
/// </summary>
/// <param name="min">The vector with minimum allowed values.</param>
/// <param name="max">The vector with maximum allowed values.</param>
/// <returns>The vector with all components clamped.</returns>
public Vector4i Clamp(Vector4i min, Vector4i max)
{
return new Vector4i
(
Mathf.Clamp(x, min.x, max.x),
Mathf.Clamp(y, min.y, max.y),
Mathf.Clamp(z, min.z, max.z),
Mathf.Clamp(w, min.w, max.w)
);
}
/// <summary>
/// Returns the length (magnitude) of this vector.
/// </summary>
/// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
public real_t Length()
{
int x2 = x * x;
int y2 = y * y;
int z2 = z * z;
int w2 = w * w;
return Mathf.Sqrt(x2 + y2 + z2 + w2);
}
/// <summary>
/// Returns the squared length (squared magnitude) of this vector.
/// This method runs faster than <see cref="Length"/>, so prefer it if
/// you need to compare vectors or need the squared length for some formula.
/// </summary>
/// <returns>The squared length of this vector.</returns>
public int LengthSquared()
{
int x2 = x * x;
int y2 = y * y;
int z2 = z * z;
int w2 = w * w;
return x2 + y2 + z2 + w2;
}
/// <summary>
/// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
/// <returns>The index of the highest axis.</returns>
public Axis MaxAxisIndex()
{
int max_index = 0;
int max_value = x;
for (int i = 1; i < 4; i++)
{
if (this[i] > max_value)
{
max_index = i;
max_value = this[i];
}
}
return (Axis)max_index;
}
/// <summary>
/// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.W"/>.
/// </summary>
/// <returns>The index of the lowest axis.</returns>
public Axis MinAxisIndex()
{
int min_index = 0;
int min_value = x;
for (int i = 1; i < 4; i++)
{
if (this[i] <= min_value)
{
min_index = i;
min_value = this[i];
}
}
return (Axis)min_index;
}
/// <summary>
/// Returns a vector with each component set to one or negative one, depending
/// on the signs of this vector's components, or zero if the component is zero,
/// by calling <see cref="Mathf.Sign(int)"/> on each component.
/// </summary>
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
public Vector4i Sign()
{
return new Vector4i(Mathf.Sign(x), Mathf.Sign(y), Mathf.Sign(z), Mathf.Sign(w));
}
// Constants
private static readonly Vector4i _zero = new Vector4i(0, 0, 0, 0);
private static readonly Vector4i _one = new Vector4i(1, 1, 1, 1);
/// <summary>
/// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
/// <value>Equivalent to <c>new Vector4i(0, 0, 0, 0)</c>.</value>
public static Vector4i Zero { get { return _zero; } }
/// <summary>
/// One vector, a vector with all components set to <c>1</c>.
/// </summary>
/// <value>Equivalent to <c>new Vector4i(1, 1, 1, 1)</c>.</value>
public static Vector4i One { get { return _one; } }
/// <summary>
/// Constructs a new <see cref="Vector4i"/> with the given components.
/// </summary>
/// <param name="x">The vector's X component.</param>
/// <param name="y">The vector's Y component.</param>
/// <param name="z">The vector's Z component.</param>
/// <param name="w">The vector's W component.</param>
public Vector4i(int x, int y, int z, int w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
/// <summary>
/// Constructs a new <see cref="Vector4i"/> from an existing <see cref="Vector4i"/>.
/// </summary>
/// <param name="vi">The existing <see cref="Vector4i"/>.</param>
public Vector4i(Vector4i vi)
{
this.x = vi.x;
this.y = vi.y;
this.z = vi.z;
this.w = vi.w;
}
/// <summary>
/// Constructs a new <see cref="Vector4i"/> from an existing <see cref="Vector4"/>
/// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>.
/// </summary>
/// <param name="v">The <see cref="Vector4"/> to convert.</param>
public Vector4i(Vector4 v)
{
this.x = Mathf.RoundToInt(v.x);
this.y = Mathf.RoundToInt(v.y);
this.z = Mathf.RoundToInt(v.z);
this.w = Mathf.RoundToInt(v.w);
}
/// <summary>
/// Adds each component of the <see cref="Vector4i"/>
/// with the components of the given <see cref="Vector4i"/>.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>The added vector.</returns>
public static Vector4i operator +(Vector4i left, Vector4i right)
{
left.x += right.x;
left.y += right.y;
left.z += right.z;
left.w += right.w;
return left;
}
/// <summary>
/// Subtracts each component of the <see cref="Vector4i"/>
/// by the components of the given <see cref="Vector4i"/>.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>The subtracted vector.</returns>
public static Vector4i operator -(Vector4i left, Vector4i right)
{
left.x -= right.x;
left.y -= right.y;
left.z -= right.z;
left.w -= right.w;
return left;
}
/// <summary>
/// Returns the negative value of the <see cref="Vector4i"/>.
/// This is the same as writing <c>new Vector4i(-v.x, -v.y, -v.z, -v.w)</c>.
/// This operation flips the direction of the vector while
/// keeping the same magnitude.
/// </summary>
/// <param name="vec">The vector to negate/flip.</param>
/// <returns>The negated/flipped vector.</returns>
public static Vector4i operator -(Vector4i vec)
{
vec.x = -vec.x;
vec.y = -vec.y;
vec.z = -vec.z;
vec.w = -vec.w;
return vec;
}
/// <summary>
/// Multiplies each component of the <see cref="Vector4i"/>
/// by the given <see langword="int"/>.
/// </summary>
/// <param name="vec">The vector to multiply.</param>
/// <param name="scale">The scale to multiply by.</param>
/// <returns>The multiplied vector.</returns>
public static Vector4i operator *(Vector4i vec, int scale)
{
vec.x *= scale;
vec.y *= scale;
vec.z *= scale;
vec.w *= scale;
return vec;
}
/// <summary>
/// Multiplies each component of the <see cref="Vector4i"/>
/// by the given <see langword="int"/>.
/// </summary>
/// <param name="scale">The scale to multiply by.</param>
/// <param name="vec">The vector to multiply.</param>
/// <returns>The multiplied vector.</returns>
public static Vector4i operator *(int scale, Vector4i vec)
{
vec.x *= scale;
vec.y *= scale;
vec.z *= scale;
vec.w *= scale;
return vec;
}
/// <summary>
/// Multiplies each component of the <see cref="Vector4i"/>
/// by the components of the given <see cref="Vector4i"/>.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>The multiplied vector.</returns>
public static Vector4i operator *(Vector4i left, Vector4i right)
{
left.x *= right.x;
left.y *= right.y;
left.z *= right.z;
left.w *= right.w;
return left;
}
/// <summary>
/// Divides each component of the <see cref="Vector4i"/>
/// by the given <see langword="int"/>.
/// </summary>
/// <param name="vec">The dividend vector.</param>
/// <param name="divisor">The divisor value.</param>
/// <returns>The divided vector.</returns>
public static Vector4i operator /(Vector4i vec, int divisor)
{
vec.x /= divisor;
vec.y /= divisor;
vec.z /= divisor;
vec.w /= divisor;
return vec;
}
/// <summary>
/// Divides each component of the <see cref="Vector4i"/>
/// by the components of the given <see cref="Vector4i"/>.
/// </summary>
/// <param name="vec">The dividend vector.</param>
/// <param name="divisorv">The divisor vector.</param>
/// <returns>The divided vector.</returns>
public static Vector4i operator /(Vector4i vec, Vector4i divisorv)
{
vec.x /= divisorv.x;
vec.y /= divisorv.y;
vec.z /= divisorv.z;
vec.w /= divisorv.w;
return vec;
}
/// <summary>
/// Gets the remainder of each component of the <see cref="Vector4i"/>
/// with the components of the given <see langword="int"/>.
/// This operation uses truncated division, which is often not desired
/// as it does not work well with negative numbers.
/// </summary>
/// <example>
/// <code>
/// GD.Print(new Vecto43i(10, -20, 30, -40) % 7); // Prints "(3, -6, 2, -5)"
/// </code>
/// </example>
/// <param name="vec">The dividend vector.</param>
/// <param name="divisor">The divisor value.</param>
/// <returns>The remainder vector.</returns>
public static Vector4i operator %(Vector4i vec, int divisor)
{
vec.x %= divisor;
vec.y %= divisor;
vec.z %= divisor;
vec.w %= divisor;
return vec;
}
/// <summary>
/// Gets the remainder of each component of the <see cref="Vector4i"/>
/// with the components of the given <see cref="Vector4i"/>.
/// This operation uses truncated division, which is often not desired
/// as it does not work well with negative numbers.
/// </summary>
/// <example>
/// <code>
/// GD.Print(new Vector4i(10, -20, 30, -40) % new Vector4i(6, 7, 8, 9)); // Prints "(4, -6, 6, -4)"
/// </code>
/// </example>
/// <param name="vec">The dividend vector.</param>
/// <param name="divisorv">The divisor vector.</param>
/// <returns>The remainder vector.</returns>
public static Vector4i operator %(Vector4i vec, Vector4i divisorv)
{
vec.x %= divisorv.x;
vec.y %= divisorv.y;
vec.z %= divisorv.z;
vec.w %= divisorv.w;
return vec;
}
/// <summary>
/// Performs a bitwise AND operation with this <see cref="Vector4i"/>
/// and the given <see langword="int"/>.
/// </summary>
/// <param name="vec">The vector to AND with.</param>
/// <param name="and">The integer to AND with.</param>
/// <returns>The result of the bitwise AND.</returns>
public static Vector4i operator &(Vector4i vec, int and)
{
vec.x &= and;
vec.y &= and;
vec.z &= and;
vec.w &= and;
return vec;
}
/// <summary>
/// Performs a bitwise AND operation with this <see cref="Vector4i"/>
/// and the given <see cref="Vector4i"/>.
/// </summary>
/// <param name="vec">The left vector to AND with.</param>
/// <param name="andv">The right vector to AND with.</param>
/// <returns>The result of the bitwise AND.</returns>
public static Vector4i operator &(Vector4i vec, Vector4i andv)
{
vec.x &= andv.x;
vec.y &= andv.y;
vec.z &= andv.z;
vec.w &= andv.w;
return vec;
}
/// <summary>
/// Returns <see langword="true"/> if the vectors are equal.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the vectors are equal.</returns>
public static bool operator ==(Vector4i left, Vector4i right)
{
return left.Equals(right);
}
/// <summary>
/// Returns <see langword="true"/> if the vectors are not equal.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the vectors are not equal.</returns>
public static bool operator !=(Vector4i left, Vector4i right)
{
return !left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Vector4i"/> vectors by first checking if
/// the X value of the <paramref name="left"/> vector is less than
/// the X value of the <paramref name="right"/> vector.
/// If the X values are exactly equal, then it repeats this check
/// with the Y, Z and finally W values of the two vectors.
/// This operator is useful for sorting vectors.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the left is less than the right.</returns>
public static bool operator <(Vector4i left, Vector4i right)
{
if (left.x == right.x)
{
if (left.y == right.y)
{
if (left.z == right.z)
{
return left.w < right.w;
}
return left.z < right.z;
}
return left.y < right.y;
}
return left.x < right.x;
}
/// <summary>
/// Compares two <see cref="Vector4i"/> vectors by first checking if
/// the X value of the <paramref name="left"/> vector is greater than
/// the X value of the <paramref name="right"/> vector.
/// If the X values are exactly equal, then it repeats this check
/// with the Y, Z and finally W values of the two vectors.
/// This operator is useful for sorting vectors.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the left is greater than the right.</returns>
public static bool operator >(Vector4i left, Vector4i right)
{
if (left.x == right.x)
{
if (left.y == right.y)
{
if (left.z == right.z)
{
return left.w > right.w;
}
return left.z > right.z;
}
return left.y > right.y;
}
return left.x > right.x;
}
/// <summary>
/// Compares two <see cref="Vector4i"/> vectors by first checking if
/// the X value of the <paramref name="left"/> vector is less than
/// or equal to the X value of the <paramref name="right"/> vector.
/// If the X values are exactly equal, then it repeats this check
/// with the Y, Z and finally W values of the two vectors.
/// This operator is useful for sorting vectors.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the left is less than or equal to the right.</returns>
public static bool operator <=(Vector4i left, Vector4i right)
{
if (left.x == right.x)
{
if (left.y == right.y)
{
if (left.z == right.z)
{
return left.w <= right.w;
}
return left.z < right.z;
}
return left.y < right.y;
}
return left.x < right.x;
}
/// <summary>
/// Compares two <see cref="Vector4i"/> vectors by first checking if
/// the X value of the <paramref name="left"/> vector is greater than
/// or equal to the X value of the <paramref name="right"/> vector.
/// If the X values are exactly equal, then it repeats this check
/// with the Y, Z and finally W values of the two vectors.
/// This operator is useful for sorting vectors.
/// </summary>
/// <param name="left">The left vector.</param>
/// <param name="right">The right vector.</param>
/// <returns>Whether or not the left is greater than or equal to the right.</returns>
public static bool operator >=(Vector4i left, Vector4i right)
{
if (left.x == right.x)
{
if (left.y == right.y)
{
if (left.z == right.z)
{
return left.w >= right.w;
}
return left.z > right.z;
}
return left.y > right.y;
}
return left.x > right.x;
}
/// <summary>
/// Converts this <see cref="Vector4i"/> to a <see cref="Vector4"/>.
/// </summary>
/// <param name="value">The vector to convert.</param>
public static implicit operator Vector4(Vector4i value)
{
return new Vector4(value.x, value.y, value.z, value.w);
}
/// <summary>
/// Converts a <see cref="Vector4"/> to a <see cref="Vector4i"/>.
/// </summary>
/// <param name="value">The vector to convert.</param>
public static explicit operator Vector4i(Vector4 value)
{
return new Vector4i(value);
}
/// <summary>
/// Returns <see langword="true"/> if the vector is equal
/// to the given object (<see paramref="obj"/>).
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Vector4i)
{
return Equals((Vector4i)obj);
}
return false;
}
/// <summary>
/// Returns <see langword="true"/> if the vectors are equal.
/// </summary>
/// <param name="other">The other vector.</param>
/// <returns>Whether or not the vectors are equal.</returns>
public bool Equals(Vector4i other)
{
return x == other.x && y == other.y && z == other.z && w == other.w;
}
/// <summary>
/// Serves as the hash function for <see cref="Vector4i"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
}
/// <summary>
/// Converts this <see cref="Vector4i"/> to a string.
/// </summary>
/// <returns>A string representation of this vector.</returns>
public override string ToString()
{
return $"({x}, {y}, {z}, {w})";
}
/// <summary>
/// Converts this <see cref="Vector4i"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this vector.</returns>
public string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}), {w.ToString(format)})";
}
}
}

View File

@ -50,6 +50,7 @@
<Compile Include="Core\NodePath.cs" />
<Compile Include="Core\Object.base.cs" />
<Compile Include="Core\Plane.cs" />
<Compile Include="Core\Projection.cs" />
<Compile Include="Core\Quaternion.cs" />
<Compile Include="Core\Rect2.cs" />
<Compile Include="Core\Rect2i.cs" />
@ -65,6 +66,8 @@
<Compile Include="Core\Vector2i.cs" />
<Compile Include="Core\Vector3.cs" />
<Compile Include="Core\Vector3i.cs" />
<Compile Include="Core\Vector4.cs" />
<Compile Include="Core\Vector4i.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<!--

View File

@ -262,7 +262,7 @@ enum {
offsetof(Vector4, z) == (sizeof(real_t) * 2) &&
offsetof(Vector4, w) == (sizeof(real_t) * 3)),
MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4i)) &&
MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4)) &&
offsetof(Vector4i, x) == (sizeof(int32_t) * 0) &&
offsetof(Vector4i, y) == (sizeof(int32_t) * 1) &&
offsetof(Vector4i, z) == (sizeof(int32_t) * 2) &&