520 lines
15 KiB
C#
520 lines
15 KiB
C#
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Godot
|
|
{
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct Basis : IEquatable<Basis>
|
|
{
|
|
private static readonly Basis identity = new Basis
|
|
(
|
|
new Vector3(1f, 0f, 0f),
|
|
new Vector3(0f, 1f, 0f),
|
|
new Vector3(0f, 0f, 1f)
|
|
);
|
|
|
|
private static readonly Basis[] orthoBases = new Basis[24]
|
|
{
|
|
new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f),
|
|
new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f),
|
|
new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f),
|
|
new Basis(0f, 1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f),
|
|
new Basis(1f, 0f, 0f, 0f, 0f, -1f, 0f, 1f, 0f),
|
|
new Basis(0f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 0f),
|
|
new Basis(-1f, 0f, 0f, 0f, 0f, 1f, 0f, 1f, 0f),
|
|
new Basis(0f, 0f, -1f, -1f, 0f, 0f, 0f, 1f, 0f),
|
|
new Basis(1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, -1f),
|
|
new Basis(0f, 1f, 0f, 1f, 0f, 0f, 0f, 0f, -1f),
|
|
new Basis(-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, -1f),
|
|
new Basis(0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, -1f),
|
|
new Basis(1f, 0f, 0f, 0f, 0f, 1f, 0f, -1f, 0f),
|
|
new Basis(0f, 0f, -1f, 1f, 0f, 0f, 0f, -1f, 0f),
|
|
new Basis(-1f, 0f, 0f, 0f, 0f, -1f, 0f, -1f, 0f),
|
|
new Basis(0f, 0f, 1f, -1f, 0f, 0f, 0f, -1f, 0f),
|
|
new Basis(0f, 0f, 1f, 0f, 1f, 0f, -1f, 0f, 0f),
|
|
new Basis(0f, -1f, 0f, 0f, 0f, 1f, -1f, 0f, 0f),
|
|
new Basis(0f, 0f, -1f, 0f, -1f, 0f, -1f, 0f, 0f),
|
|
new Basis(0f, 1f, 0f, 0f, 0f, -1f, -1f, 0f, 0f),
|
|
new Basis(0f, 0f, 1f, 0f, -1f, 0f, 1f, 0f, 0f),
|
|
new Basis(0f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 0f),
|
|
new Basis(0f, 0f, -1f, 0f, 1f, 0f, 1f, 0f, 0f),
|
|
new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f)
|
|
};
|
|
|
|
public Vector3 x;
|
|
public Vector3 y;
|
|
public Vector3 z;
|
|
|
|
public static Basis Identity
|
|
{
|
|
get { return identity; }
|
|
}
|
|
|
|
public Vector3 Scale
|
|
{
|
|
get
|
|
{
|
|
return new Vector3
|
|
(
|
|
new Vector3(this[0, 0], this[1, 0], this[2, 0]).length(),
|
|
new Vector3(this[0, 1], this[1, 1], this[2, 1]).length(),
|
|
new Vector3(this[0, 2], this[1, 2], this[2, 2]).length()
|
|
);
|
|
}
|
|
}
|
|
|
|
public Vector3 this[int index]
|
|
{
|
|
get
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
return x;
|
|
case 1:
|
|
return y;
|
|
case 2:
|
|
return z;
|
|
default:
|
|
throw new IndexOutOfRangeException();
|
|
}
|
|
}
|
|
set
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
x = value;
|
|
return;
|
|
case 1:
|
|
y = value;
|
|
return;
|
|
case 2:
|
|
z = value;
|
|
return;
|
|
default:
|
|
throw new IndexOutOfRangeException();
|
|
}
|
|
}
|
|
}
|
|
|
|
public float this[int index, int axis]
|
|
{
|
|
get
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
return x[axis];
|
|
case 1:
|
|
return y[axis];
|
|
case 2:
|
|
return z[axis];
|
|
default:
|
|
throw new IndexOutOfRangeException();
|
|
}
|
|
}
|
|
set
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
x[axis] = value;
|
|
return;
|
|
case 1:
|
|
y[axis] = value;
|
|
return;
|
|
case 2:
|
|
z[axis] = value;
|
|
return;
|
|
default:
|
|
throw new IndexOutOfRangeException();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static Basis create_from_axes(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
|
|
{
|
|
return new Basis
|
|
(
|
|
new Vector3(xAxis.x, yAxis.x, zAxis.x),
|
|
new Vector3(xAxis.y, yAxis.y, zAxis.y),
|
|
new Vector3(xAxis.z, yAxis.z, zAxis.z)
|
|
);
|
|
}
|
|
|
|
public float determinant()
|
|
{
|
|
return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) -
|
|
this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) +
|
|
this[2, 0] * (this[0, 1] * this[1, 2] - this[1, 1] * this[0, 2]);
|
|
}
|
|
|
|
public Vector3 get_axis(int axis)
|
|
{
|
|
return new Vector3(this[0, axis], this[1, axis], this[2, axis]);
|
|
}
|
|
|
|
public Vector3 get_euler()
|
|
{
|
|
Basis m = this.orthonormalized();
|
|
|
|
Vector3 euler;
|
|
euler.z = 0.0f;
|
|
|
|
float mxy = m.y[2];
|
|
|
|
|
|
if (mxy < 1.0f)
|
|
{
|
|
if (mxy > -1.0f)
|
|
{
|
|
euler.x = Mathf.asin(-mxy);
|
|
euler.y = Mathf.atan2(m.x[2], m.z[2]);
|
|
euler.z = Mathf.atan2(m.y[0], m.y[1]);
|
|
}
|
|
else
|
|
{
|
|
euler.x = Mathf.PI * 0.5f;
|
|
euler.y = -Mathf.atan2(-m.x[1], m.x[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
euler.x = -Mathf.PI * 0.5f;
|
|
euler.y = -Mathf.atan2(m.x[1], m.x[0]);
|
|
}
|
|
|
|
return euler;
|
|
}
|
|
|
|
public int get_orthogonal_index()
|
|
{
|
|
Basis orth = this;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
float v = orth[i, j];
|
|
|
|
if (v > 0.5f)
|
|
v = 1.0f;
|
|
else if (v < -0.5f)
|
|
v = -1.0f;
|
|
else
|
|
v = 0f;
|
|
|
|
orth[i, j] = v;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 24; i++)
|
|
{
|
|
if (orthoBases[i] == orth)
|
|
return i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public Basis inverse()
|
|
{
|
|
Basis inv = this;
|
|
|
|
float[] co = new float[3]
|
|
{
|
|
inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1],
|
|
inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2],
|
|
inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0]
|
|
};
|
|
|
|
float det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2];
|
|
|
|
if (det == 0)
|
|
{
|
|
return new Basis
|
|
(
|
|
float.NaN, float.NaN, float.NaN,
|
|
float.NaN, float.NaN, float.NaN,
|
|
float.NaN, float.NaN, float.NaN
|
|
);
|
|
}
|
|
|
|
float s = 1.0f / det;
|
|
|
|
inv = new Basis
|
|
(
|
|
co[0] * s,
|
|
inv[0, 2] * inv[2, 1] - inv[0, 1] * inv[2, 2] * s,
|
|
inv[0, 1] * inv[1, 2] - inv[0, 2] * inv[1, 1] * s,
|
|
co[1] * s,
|
|
inv[0, 0] * inv[2, 2] - inv[0, 2] * inv[2, 0] * s,
|
|
inv[0, 2] * inv[1, 0] - inv[0, 0] * inv[1, 2] * s,
|
|
co[2] * s,
|
|
inv[0, 1] * inv[2, 0] - inv[0, 0] * inv[2, 1] * s,
|
|
inv[0, 0] * inv[1, 1] - inv[0, 1] * inv[1, 0] * s
|
|
);
|
|
|
|
return inv;
|
|
}
|
|
|
|
public Basis orthonormalized()
|
|
{
|
|
Vector3 xAxis = get_axis(0);
|
|
Vector3 yAxis = get_axis(1);
|
|
Vector3 zAxis = get_axis(2);
|
|
|
|
xAxis.normalize();
|
|
yAxis = (yAxis - xAxis * (xAxis.dot(yAxis)));
|
|
yAxis.normalize();
|
|
zAxis = (zAxis - xAxis * (xAxis.dot(zAxis)) - yAxis * (yAxis.dot(zAxis)));
|
|
zAxis.normalize();
|
|
|
|
return Basis.create_from_axes(xAxis, yAxis, zAxis);
|
|
}
|
|
|
|
public Basis rotated(Vector3 axis, float phi)
|
|
{
|
|
return new Basis(axis, phi) * this;
|
|
}
|
|
|
|
public Basis scaled(Vector3 scale)
|
|
{
|
|
Basis m = this;
|
|
|
|
m[0, 0] *= scale.x;
|
|
m[0, 1] *= scale.x;
|
|
m[0, 2] *= scale.x;
|
|
m[1, 0] *= scale.y;
|
|
m[1, 1] *= scale.y;
|
|
m[1, 2] *= scale.y;
|
|
m[2, 0] *= scale.z;
|
|
m[2, 1] *= scale.z;
|
|
m[2, 2] *= scale.z;
|
|
|
|
return m;
|
|
}
|
|
|
|
public float tdotx(Vector3 with)
|
|
{
|
|
return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2];
|
|
}
|
|
|
|
public float tdoty(Vector3 with)
|
|
{
|
|
return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2];
|
|
}
|
|
|
|
public float tdotz(Vector3 with)
|
|
{
|
|
return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2];
|
|
}
|
|
|
|
public Basis transposed()
|
|
{
|
|
Basis tr = this;
|
|
|
|
float temp = this[0, 1];
|
|
this[0, 1] = this[1, 0];
|
|
this[1, 0] = temp;
|
|
|
|
temp = this[0, 2];
|
|
this[0, 2] = this[2, 0];
|
|
this[2, 0] = temp;
|
|
|
|
temp = this[1, 2];
|
|
this[1, 2] = this[2, 1];
|
|
this[2, 1] = temp;
|
|
|
|
return tr;
|
|
}
|
|
|
|
public Vector3 xform(Vector3 v)
|
|
{
|
|
return new Vector3
|
|
(
|
|
this[0].dot(v),
|
|
this[1].dot(v),
|
|
this[2].dot(v)
|
|
);
|
|
}
|
|
|
|
public Vector3 xform_inv(Vector3 v)
|
|
{
|
|
return new Vector3
|
|
(
|
|
(this[0, 0] * v.x) + (this[1, 0] * v.y) + (this[2, 0] * v.z),
|
|
(this[0, 1] * v.x) + (this[1, 1] * v.y) + (this[2, 1] * v.z),
|
|
(this[0, 2] * v.x) + (this[1, 2] * v.y) + (this[2, 2] * v.z)
|
|
);
|
|
}
|
|
|
|
public Quat Quat() {
|
|
float trace = x[0] + y[1] + z[2];
|
|
|
|
if (trace > 0.0f) {
|
|
float s = Mathf.sqrt(trace + 1.0f) * 2f;
|
|
float inv_s = 1f / s;
|
|
return new Quat(
|
|
(z[1] - y[2]) * inv_s,
|
|
(x[2] - z[0]) * inv_s,
|
|
(y[0] - x[1]) * inv_s,
|
|
s * 0.25f
|
|
);
|
|
} else if (x[0] > y[1] && x[0] > z[2]) {
|
|
float s = Mathf.sqrt(x[0] - y[1] - z[2] + 1.0f) * 2f;
|
|
float inv_s = 1f / s;
|
|
return new Quat(
|
|
s * 0.25f,
|
|
(x[1] + y[0]) * inv_s,
|
|
(x[2] + z[0]) * inv_s,
|
|
(z[1] - y[2]) * inv_s
|
|
);
|
|
} else if (y[1] > z[2]) {
|
|
float s = Mathf.sqrt(-x[0] + y[1] - z[2] + 1.0f) * 2f;
|
|
float inv_s = 1f / s;
|
|
return new Quat(
|
|
(x[1] + y[0]) * inv_s,
|
|
s * 0.25f,
|
|
(y[2] + z[1]) * inv_s,
|
|
(x[2] - z[0]) * inv_s
|
|
);
|
|
} else {
|
|
float s = Mathf.sqrt(-x[0] - y[1] + z[2] + 1.0f) * 2f;
|
|
float inv_s = 1f / s;
|
|
return new Quat(
|
|
(x[2] + z[0]) * inv_s,
|
|
(y[2] + z[1]) * inv_s,
|
|
s * 0.25f,
|
|
(y[0] - x[1]) * inv_s
|
|
);
|
|
}
|
|
}
|
|
|
|
public Basis(Quat quat)
|
|
{
|
|
float s = 2.0f / quat.length_squared();
|
|
|
|
float xs = quat.x * s;
|
|
float ys = quat.y * s;
|
|
float zs = quat.z * s;
|
|
float wx = quat.w * xs;
|
|
float wy = quat.w * ys;
|
|
float wz = quat.w * zs;
|
|
float xx = quat.x * xs;
|
|
float xy = quat.x * ys;
|
|
float xz = quat.x * zs;
|
|
float yy = quat.y * ys;
|
|
float yz = quat.y * zs;
|
|
float zz = quat.z * zs;
|
|
|
|
this.x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
|
|
this.y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
|
|
this.z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
|
|
}
|
|
|
|
public Basis(Vector3 axis, float phi)
|
|
{
|
|
Vector3 axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
|
|
|
|
float cosine = Mathf.cos(phi);
|
|
float sine = Mathf.sin(phi);
|
|
|
|
this.x = new Vector3
|
|
(
|
|
axis_sq.x + cosine * (1.0f - axis_sq.x),
|
|
axis.x * axis.y * (1.0f - cosine) - axis.z * sine,
|
|
axis.z * axis.x * (1.0f - cosine) + axis.y * sine
|
|
);
|
|
|
|
this.y = new Vector3
|
|
(
|
|
axis.x * axis.y * (1.0f - cosine) + axis.z * sine,
|
|
axis_sq.y + cosine * (1.0f - axis_sq.y),
|
|
axis.y * axis.z * (1.0f - cosine) - axis.x * sine
|
|
);
|
|
|
|
this.z = new Vector3
|
|
(
|
|
axis.z * axis.x * (1.0f - cosine) - axis.y * sine,
|
|
axis.y * axis.z * (1.0f - cosine) + axis.x * sine,
|
|
axis_sq.z + cosine * (1.0f - axis_sq.z)
|
|
);
|
|
}
|
|
|
|
public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
|
|
{
|
|
this.x = xAxis;
|
|
this.y = yAxis;
|
|
this.z = zAxis;
|
|
}
|
|
|
|
public Basis(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz)
|
|
{
|
|
this.x = new Vector3(xx, xy, xz);
|
|
this.y = new Vector3(yx, yy, yz);
|
|
this.z = new Vector3(zx, zy, zz);
|
|
}
|
|
|
|
public static Basis operator *(Basis left, Basis right)
|
|
{
|
|
return new Basis
|
|
(
|
|
right.tdotx(left[0]), right.tdoty(left[0]), right.tdotz(left[0]),
|
|
right.tdotx(left[1]), right.tdoty(left[1]), right.tdotz(left[1]),
|
|
right.tdotx(left[2]), right.tdoty(left[2]), right.tdotz(left[2])
|
|
);
|
|
}
|
|
|
|
public static bool operator ==(Basis left, Basis right)
|
|
{
|
|
return left.Equals(right);
|
|
}
|
|
|
|
public static bool operator !=(Basis left, Basis right)
|
|
{
|
|
return !left.Equals(right);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (obj is Basis)
|
|
{
|
|
return Equals((Basis)obj);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool Equals(Basis other)
|
|
{
|
|
return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format("({0}, {1}, {2})", new object[]
|
|
{
|
|
this.x.ToString(),
|
|
this.y.ToString(),
|
|
this.z.ToString()
|
|
});
|
|
}
|
|
|
|
public string ToString(string format)
|
|
{
|
|
return String.Format("({0}, {1}, {2})", new object[]
|
|
{
|
|
this.x.ToString(format),
|
|
this.y.ToString(format),
|
|
this.z.ToString(format)
|
|
});
|
|
}
|
|
}
|
|
}
|