diff --git a/BeefLibs/Beefy2D/src/geom/BoundingBox.bf b/BeefLibs/Beefy2D/src/geom/BoundingBox.bf
new file mode 100644
index 00000000..63214a50
--- /dev/null
+++ b/BeefLibs/Beefy2D/src/geom/BoundingBox.bf
@@ -0,0 +1,14 @@
+// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA),
+// released under the Microsoft Public License
+
+namespace Beefy.geom
+{
+ class BoundingBox
+ {
+ public Vector3 Min;
+ public Vector3 Max;
+ public const int CornerCount = 8;
+
+
+ }
+}
diff --git a/BeefLibs/Beefy2D/src/geom/Matrix4.bf b/BeefLibs/Beefy2D/src/geom/Matrix4.bf
index c2c5cbb2..7a1a2294 100644
--- a/BeefLibs/Beefy2D/src/geom/Matrix4.bf
+++ b/BeefLibs/Beefy2D/src/geom/Matrix4.bf
@@ -55,6 +55,19 @@ namespace Beefy.geom
this.m33 = m33;
}
+ public static Matrix4 CreateFromColumnMajor(
+ float m00, float m10, float m20, float m30,
+ float m01, float m11, float m21, float m31,
+ float m02, float m12, float m22, float m32,
+ float m03, float m13, float m23, float m33)
+ {
+ return .(
+ m00, m01, m02, m03,
+ m10, m11, m12, m13,
+ m20, m21, m22, m23,
+ m30, m31, m32, m33);
+ }
+
public static Matrix4 CreatePerspective(float width, float height, float nearPlaneDistance, float farPlaneDistance)
{
Matrix4 matrix;
@@ -190,25 +203,23 @@ namespace Beefy.geom
public static Matrix4 Multiply(Matrix4 m1, Matrix4 m2)
{
Matrix4 r;
- r.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20 + m1.m03 * m2.m30;
- r.m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21 + m1.m03 * m2.m31;
- r.m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22 + m1.m03 * m2.m32;
- r.m03 = m1.m00 * m2.m03 + m1.m01 * m2.m13 + m1.m02 * m2.m23 + m1.m03 * m2.m33;
- r.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20 + m1.m13 * m2.m30;
- r.m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21 + m1.m13 * m2.m31;
- r.m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22 + m1.m13 * m2.m32;
- r.m13 = m1.m10 * m2.m03 + m1.m11 * m2.m13 + m1.m12 * m2.m23 + m1.m13 * m2.m33;
-
- r.m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20 + m1.m23 * m2.m30;
- r.m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21 + m1.m23 * m2.m31;
- r.m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22 + m1.m23 * m2.m32;
- r.m23 = m1.m20 * m2.m03 + m1.m21 * m2.m13 + m1.m22 * m2.m23 + m1.m23 * m2.m33;
-
- r.m30 = m1.m30 * m2.m00 + m1.m31 * m2.m10 + m1.m32 * m2.m20 + m1.m33 * m2.m30;
- r.m31 = m1.m30 * m2.m01 + m1.m31 * m2.m11 + m1.m32 * m2.m21 + m1.m33 * m2.m31;
- r.m32 = m1.m30 * m2.m02 + m1.m31 * m2.m12 + m1.m32 * m2.m22 + m1.m33 * m2.m32;
- r.m33 = m1.m30 * m2.m03 + m1.m31 * m2.m13 + m1.m32 * m2.m23 + m1.m33 * m2.m33;
+ r.m00 = (((m1.m00 * m2.m00) + (m1.m10 * m2.m01)) + (m1.m20 * m2.m02)) + (m1.m30 * m2.m03);
+ r.m10 = (((m1.m00 * m2.m10) + (m1.m10 * m2.m11)) + (m1.m20 * m2.m12)) + (m1.m30 * m2.m13);
+ r.m20 = (((m1.m00 * m2.m20) + (m1.m10 * m2.m21)) + (m1.m20 * m2.m22)) + (m1.m30 * m2.m23);
+ r.m30 = (((m1.m00 * m2.m30) + (m1.m10 * m2.m31)) + (m1.m20 * m2.m32)) + (m1.m30 * m2.m33);
+ r.m01 = (((m1.m01 * m2.m00) + (m1.m11 * m2.m01)) + (m1.m21 * m2.m02)) + (m1.m31 * m2.m03);
+ r.m11 = (((m1.m01 * m2.m10) + (m1.m11 * m2.m11)) + (m1.m21 * m2.m12)) + (m1.m31 * m2.m13);
+ r.m21 = (((m1.m01 * m2.m20) + (m1.m11 * m2.m21)) + (m1.m21 * m2.m22)) + (m1.m31 * m2.m23);
+ r.m31 = (((m1.m01 * m2.m30) + (m1.m11 * m2.m31)) + (m1.m21 * m2.m32)) + (m1.m31 * m2.m33);
+ r.m02 = (((m1.m02 * m2.m00) + (m1.m12 * m2.m01)) + (m1.m22 * m2.m02)) + (m1.m32 * m2.m03);
+ r.m12 = (((m1.m02 * m2.m10) + (m1.m12 * m2.m11)) + (m1.m22 * m2.m12)) + (m1.m32 * m2.m13);
+ r.m22 = (((m1.m02 * m2.m20) + (m1.m12 * m2.m21)) + (m1.m22 * m2.m22)) + (m1.m32 * m2.m23);
+ r.m32 = (((m1.m02 * m2.m30) + (m1.m12 * m2.m31)) + (m1.m22 * m2.m32)) + (m1.m32 * m2.m33);
+ r.m03 = (((m1.m03 * m2.m00) + (m1.m13 * m2.m01)) + (m1.m23 * m2.m02)) + (m1.m33 * m2.m03);
+ r.m13 = (((m1.m03 * m2.m10) + (m1.m13 * m2.m11)) + (m1.m23 * m2.m12)) + (m1.m33 * m2.m13);
+ r.m23 = (((m1.m03 * m2.m20) + (m1.m13 * m2.m21)) + (m1.m23 * m2.m22)) + (m1.m33 * m2.m23);
+ r.m33 = (((m1.m03 * m2.m30) + (m1.m13 * m2.m31)) + (m1.m23 * m2.m32)) + (m1.m33 * m2.m33);
return r;
}
@@ -489,5 +500,730 @@ namespace Beefy.geom
r20, r21, r22, r23,
0, 0, 0, 1);
}
+
+ public static void Invert(Matrix4 matrix, out Matrix4 result)
+ {
+ float num1 = matrix.m00;
+ float num2 = matrix.m10;
+ float num3 = matrix.m20;
+ float num4 = matrix.m30;
+ float num5 = matrix.m01;
+ float num6 = matrix.m11;
+ float num7 = matrix.m21;
+ float num8 = matrix.m31;
+ float num9 = matrix.m02;
+ float num10 = matrix.m12;
+ float num11 = matrix.m22;
+ float num12 = matrix.m32;
+ float num13 = matrix.m03;
+ float num14 = matrix.m13;
+ float num15 = matrix.m23;
+ float num16 = matrix.m33;
+ float num17 = (float) ((double) num11 * (double) num16 - (double) num12 * (double) num15);
+ float num18 = (float) ((double) num10 * (double) num16 - (double) num12 * (double) num14);
+ float num19 = (float) ((double) num10 * (double) num15 - (double) num11 * (double) num14);
+ float num20 = (float) ((double) num9 * (double) num16 - (double) num12 * (double) num13);
+ float num21 = (float) ((double) num9 * (double) num15 - (double) num11 * (double) num13);
+ float num22 = (float) ((double) num9 * (double) num14 - (double) num10 * (double) num13);
+ float num23 = (float) ((double) num6 * (double) num17 - (double) num7 * (double) num18 + (double) num8 * (double) num19);
+ float num24 = (float) -((double) num5 * (double) num17 - (double) num7 * (double) num20 + (double) num8 * (double) num21);
+ float num25 = (float) ((double) num5 * (double) num18 - (double) num6 * (double) num20 + (double) num8 * (double) num22);
+ float num26 = (float) -((double) num5 * (double) num19 - (double) num6 * (double) num21 + (double) num7 * (double) num22);
+ float num27 = (float) (1.0 / ((double) num1 * (double) num23 + (double) num2 * (double) num24 + (double) num3 * (double) num25 + (double) num4 * (double) num26));
+
+ result.m00 = num23 * num27;
+ result.m01 = num24 * num27;
+ result.m02 = num25 * num27;
+ result.m03 = num26 * num27;
+ result.m10 = (float)(-((double) num2 * (double) num17 - (double) num3 * (double) num18 + (double) num4 * (double) num19) * num27);
+ result.m11 = (float) ((double) num1 * (double) num17 - (double) num3 * (double) num20 + (double) num4 * (double) num21) * num27;
+ result.m12 = (float)(-((double) num1 * (double) num18 - (double) num2 * (double) num20 + (double) num4 * (double) num22) * num27);
+ result.m13 = (float) ((double) num1 * (double) num19 - (double) num2 * (double) num21 + (double) num3 * (double) num22) * num27;
+ float num28 = (float) ((double) num7 * (double) num16 - (double) num8 * (double) num15);
+ float num29 = (float) ((double) num6 * (double) num16 - (double) num8 * (double) num14);
+ float num30 = (float) ((double) num6 * (double) num15 - (double) num7 * (double) num14);
+ float num31 = (float) ((double) num5 * (double) num16 - (double) num8 * (double) num13);
+ float num32 = (float) ((double) num5 * (double) num15 - (double) num7 * (double) num13);
+ float num33 = (float) ((double) num5 * (double) num14 - (double) num6 * (double) num13);
+ result.m20 = (float) ((double) num2 * (double) num28 - (double) num3 * (double) num29 + (double) num4 * (double) num30) * num27;
+ result.m21 = (float)(-((double) num1 * (double) num28 - (double) num3 * (double) num31 + (double) num4 * (double) num32) * num27);
+ result.m22 = (float) ((double) num1 * (double) num29 - (double) num2 * (double) num31 + (double) num4 * (double) num33) * num27;
+ result.m23 = (float)(-((double) num1 * (double) num30 - (double) num2 * (double) num32 + (double) num3 * (double) num33) * num27);
+ float num34 = (float) ((double) num7 * (double) num12 - (double) num8 * (double) num11);
+ float num35 = (float) ((double) num6 * (double) num12 - (double) num8 * (double) num10);
+ float num36 = (float) ((double) num6 * (double) num11 - (double) num7 * (double) num10);
+ float num37 = (float) ((double) num5 * (double) num12 - (double) num8 * (double) num9);
+ float num38 = (float) ((double) num5 * (double) num11 - (double) num7 * (double) num9);
+ float num39 = (float) ((double) num5 * (double) num10 - (double) num6 * (double) num9);
+ result.m30 = (float)(-((double) num2 * (double) num34 - (double) num3 * (double) num35 + (double) num4 * (double) num36) * num27);
+ result.m31 = (float) ((double) num1 * (double) num34 - (double) num3 * (double) num37 + (double) num4 * (double) num38) * num27;
+ result.m32 = (float)(-((double) num1 * (double) num35 - (double) num2 * (double) num37 + (double) num4 * (double) num39) * num27);
+ result.m33 = (float) ((double) num1 * (double) num36 - (double) num2 * (double) num38 + (double) num3 * (double) num39) * num27;
+
+ /*
+
+
+ ///
+ // Use Laplace expansion theorem to calculate the inverse of a 4x4 matrix
+ //
+ // 1. Calculate the 2x2 determinants needed the 4x4 determinant based on the 2x2 determinants
+ // 3. Create the adjugate matrix, which satisfies: A * adj(A) = det(A) * I
+ // 4. Divide adjugate matrix with the determinant to find the inverse
+
+ float det1, det2, det3, det4, det5, det6, det7, det8, det9, det10, det11, det12;
+ float detMatrix;
+ FindDeterminants(ref matrix, out detMatrix, out det1, out det2, out det3, out det4, out det5, out det6,
+ out det7, out det8, out det9, out det10, out det11, out det12);
+
+ float invDetMatrix = 1f / detMatrix;
+
+ Matrix ret; // Allow for matrix and result to point to the same structure
+
+ ret.M11 = (matrix.M22*det12 - matrix.M23*det11 + matrix.M24*det10) * invDetMatrix;
+ ret.M12 = (-matrix.M12*det12 + matrix.M13*det11 - matrix.M14*det10) * invDetMatrix;
+ ret.M13 = (matrix.M42*det6 - matrix.M43*det5 + matrix.M44*det4) * invDetMatrix;
+ ret.M14 = (-matrix.M32*det6 + matrix.M33*det5 - matrix.M34*det4) * invDetMatrix;
+ ret.M21 = (-matrix.M21*det12 + matrix.M23*det9 - matrix.M24*det8) * invDetMatrix;
+ ret.M22 = (matrix.M11*det12 - matrix.M13*det9 + matrix.M14*det8) * invDetMatrix;
+ ret.M23 = (-matrix.M41*det6 + matrix.M43*det3 - matrix.M44*det2) * invDetMatrix;
+ ret.M24 = (matrix.M31*det6 - matrix.M33*det3 + matrix.M34*det2) * invDetMatrix;
+ ret.M31 = (matrix.M21*det11 - matrix.M22*det9 + matrix.M24*det7) * invDetMatrix;
+ ret.M32 = (-matrix.M11*det11 + matrix.M12*det9 - matrix.M14*det7) * invDetMatrix;
+ ret.M33 = (matrix.M41*det5 - matrix.M42*det3 + matrix.M44*det1) * invDetMatrix;
+ ret.M34 = (-matrix.M31*det5 + matrix.M32*det3 - matrix.M34*det1) * invDetMatrix;
+ ret.M41 = (-matrix.M21*det10 + matrix.M22*det8 - matrix.M23*det7) * invDetMatrix;
+ ret.M42 = (matrix.M11*det10 - matrix.M12*det8 + matrix.M13*det7) * invDetMatrix;
+ ret.M43 = (-matrix.M41*det4 + matrix.M42*det2 - matrix.M43*det1) * invDetMatrix;
+ ret.M44 = (matrix.M31*det4 - matrix.M32*det2 + matrix.M33*det1) * invDetMatrix;
+
+ result = ret;
+ */
+ }
+
+ public static Matrix4 Invert(Matrix4 matrix)
+ {
+ Invert(matrix, var outMatrix);
+ return outMatrix;
+ }
+
+ ///
+ /// Decomposes this matrix to translation, rotation and scale elements. Returns true if matrix can be decomposed; false otherwise.
+ ///
+ /// Scale vector as an output parameter.
+ /// Rotation quaternion as an output parameter.
+ /// Translation vector as an output parameter.
+ /// true if matrix can be decomposed; false otherwise.
+ public bool Decompose(
+ out Vector3 scale,
+ out Quaternion rotation,
+ out Vector3 translation
+ ) {
+ translation.mX = m03;
+ translation.mY = m13;
+ translation.mZ = m23;
+
+ float xs = (Math.Sign(m00 * m10 * m20 * m30) < 0) ? -1 : 1;
+ float ys = (Math.Sign(m01 * m11 * m21 * m31) < 0) ? -1 : 1;
+ float zs = (Math.Sign(m02 * m12 * m22 * m32) < 0) ? -1 : 1;
+
+ scale.mX = xs * (float) Math.Sqrt(m00 * m00 + m10 * m10 + m20 * m20);
+ scale.mY = ys * (float) Math.Sqrt(m01 * m01 + m11 * m11 + m21 * m21);
+ scale.mZ = zs * (float) Math.Sqrt(m02 * m02 + m12 * m12 + m22 * m22);
+
+ if (Math.WithinEpsilon(scale.mX, 0.0f) ||
+ Math.WithinEpsilon(scale.mY, 0.0f) ||
+ Math.WithinEpsilon(scale.mZ, 0.0f) )
+ {
+ rotation = Quaternion.Identity;
+ return false;
+ }
+
+ Matrix4 m1 = Matrix4.CreateFromColumnMajor(
+ m00 / scale.mX, m10 / scale.mX, m20 / scale.mX, 0,
+ m01 / scale.mY, m11 / scale.mY, m21 / scale.mY, 0,
+ m02 / scale.mZ, m12 / scale.mZ, m22 / scale.mZ, 0,
+ 0, 0, 0, 1
+ );
+
+ rotation = Quaternion.CreateFromRotationMatrix(m1);
+ return true;
+ }
+
+ ///
+ /// Returns a determinant of this matrix.
+ ///
+ /// Determinant of this matrix
+ /// See more about determinant here - http://en.wikipedia.org/wiki/Determinant.
+ ///
+ public float Determinant()
+ {
+ float num18 = (m22 * m33) - (m32 * m23);
+ float num17 = (m12 * m33) - (m32 * m13);
+ float num16 = (m12 * m23) - (m22 * m13);
+ float num15 = (m02 * m33) - (m32 * m03);
+ float num14 = (m02 * m23) - (m22 * m03);
+ float num13 = (m02 * m13) - (m12 * m03);
+ return (
+ (
+ (
+ (m00 * (((m11 * num18) - (m21 * num17)) + (m31 * num16))) -
+ (m10 * (((m01 * num18) - (m21 * num15)) + (m31 * num14)))
+ ) + (m20 * (((m01 * num17) - (m11 * num15)) + (m31 * num13)))
+ ) - (m30 * (((m01 * num16) - (m11 * num14)) + (m21 * num13)))
+ );
+ }
+
+ ///
+ /// Creates a new matrix for spherical billboarding that rotates around specified object position.
+ ///
+ /// Position of billboard object. It will rotate around that vector.
+ /// The camera position.
+ /// The camera up vector.
+ /// Optional camera forward vector.
+ /// The matrix for spherical billboarding.
+ public static Matrix4 CreateBillboard(
+ Vector3 objectPosition,
+ Vector3 cameraPosition,
+ Vector3 cameraUpVector,
+ Nullable cameraForwardVector
+ ) {
+ Matrix4 result;
+
+ // Delegate to the other overload of the function to do the work
+ CreateBillboard(
+ objectPosition,
+ cameraPosition,
+ cameraUpVector,
+ cameraForwardVector,
+ out result
+ );
+
+ return result;
+ }
+
+ ///
+ /// Creates a new matrix for spherical billboarding that rotates around specified object position.
+ ///
+ /// Position of billboard object. It will rotate around that vector.
+ /// The camera position.
+ /// The camera up vector.
+ /// Optional camera forward vector.
+ /// The matrix for spherical billboarding as an output parameter.
+ public static void CreateBillboard(
+ Vector3 objectPosition,
+ Vector3 cameraPosition,
+ Vector3 cameraUpVector,
+ Vector3? cameraForwardVector,
+ out Matrix4 result
+ ) {
+ Vector3 vector;
+ Vector3 vector2;
+ Vector3 vector3;
+ vector.mX = objectPosition.mX - cameraPosition.mX;
+ vector.mY = objectPosition.mY - cameraPosition.mY;
+ vector.mZ = objectPosition.mZ - cameraPosition.mZ;
+ float num = vector.LengthSquared;
+ if (num < 0.0001f)
+ {
+ vector = cameraForwardVector.HasValue ?
+ -cameraForwardVector.Value :
+ Vector3.Forward;
+ }
+ else
+ {
+ vector *= (float) (1f / ((float) Math.Sqrt((double) num)));
+ }
+ vector3 = Vector3.Cross(cameraUpVector, vector);
+ vector3.Normalize();
+ vector2 = Vector3.Cross(vector, vector3);
+ result.m00 = vector3.mX;
+ result.m10 = vector3.mY;
+ result.m20 = vector3.mZ;
+ result.m30 = 0;
+ result.m01 = vector2.mX;
+ result.m11 = vector2.mY;
+ result.m21 = vector2.mZ;
+ result.m31 = 0;
+ result.m02 = vector.mX;
+ result.m12 = vector.mY;
+ result.m22 = vector.mZ;
+ result.m32 = 0;
+ result.m03 = objectPosition.mX;
+ result.m13 = objectPosition.mY;
+ result.m23 = objectPosition.mZ;
+ result.m33 = 1;
+ }
+
+ ///
+ /// Creates a new matrix for cylindrical billboarding that rotates around specified axis.
+ ///
+ /// Object position the billboard will rotate around.
+ /// Camera position.
+ /// Axis of billboard for rotation.
+ /// Optional camera forward vector.
+ /// Optional object forward vector.
+ /// The matrix for cylindrical billboarding.
+ public static Matrix4 CreateConstrainedBillboard(
+ Vector3 objectPosition,
+ Vector3 cameraPosition,
+ Vector3 rotateAxis,
+ Nullable cameraForwardVector,
+ Nullable objectForwardVector
+ ) {
+ Matrix4 result;
+ CreateConstrainedBillboard(
+ objectPosition,
+ cameraPosition,
+ rotateAxis,
+ cameraForwardVector,
+ objectForwardVector,
+ out result
+ );
+ return result;
+ }
+
+ ///
+ /// Creates a new matrix for cylindrical billboarding that rotates around specified axis.
+ ///
+ /// Object position the billboard will rotate around.
+ /// Camera position.
+ /// Axis of billboard for rotation.
+ /// Optional camera forward vector.
+ /// Optional object forward vector.
+ /// The matrix for cylindrical billboarding as an output parameter.
+ public static void CreateConstrainedBillboard(
+ Vector3 objectPosition,
+ Vector3 cameraPosition,
+ Vector3 rotateAxis,
+ Vector3? cameraForwardVector,
+ Vector3? objectForwardVector,
+ out Matrix4 result
+ ) {
+ float num;
+ Vector3 vector;
+ Vector3 vector2;
+ Vector3 vector3;
+ vector2.mX = objectPosition.mX - cameraPosition.mX;
+ vector2.mY = objectPosition.mY - cameraPosition.mY;
+ vector2.mZ = objectPosition.mZ - cameraPosition.mZ;
+ float num2 = vector2.LengthSquared;
+ if (num2 < 0.0001f)
+ {
+ vector2 = cameraForwardVector.HasValue ?
+ -cameraForwardVector.Value :
+ Vector3.Forward;
+ }
+ else
+ {
+ vector2 *= (float) (1f / ((float) Math.Sqrt((double) num2)));
+ }
+ Vector3 vector4 = rotateAxis;
+ num = Vector3.Dot(rotateAxis, vector2);
+ if (Math.Abs(num) > 0.9982547f)
+ {
+ if (objectForwardVector.HasValue)
+ {
+ vector = objectForwardVector.Value;
+ num = Vector3.Dot(rotateAxis, vector);
+ if (Math.Abs(num) > 0.9982547f)
+ {
+ num = (
+ (rotateAxis.mX * Vector3.Forward.mX) +
+ (rotateAxis.mY * Vector3.Forward.mY)
+ ) + (rotateAxis.mZ * Vector3.Forward.mZ);
+ vector = (Math.Abs(num) > 0.9982547f) ?
+ Vector3.Right :
+ Vector3.Forward;
+ }
+ }
+ else
+ {
+ num = (
+ (rotateAxis.mX * Vector3.Forward.mX) +
+ (rotateAxis.mY * Vector3.Forward.mY)
+ ) + (rotateAxis.mZ * Vector3.Forward.mZ);
+ vector = (Math.Abs(num) > 0.9982547f) ?
+ Vector3.Right :
+ Vector3.Forward;
+ }
+ vector3 = Vector3.Cross(rotateAxis, vector);
+ vector3.Normalize();
+ vector = Vector3.Cross(vector3, rotateAxis);
+ vector.Normalize();
+ }
+ else
+ {
+ vector3 = Vector3.Cross(rotateAxis, vector2);
+ vector3.Normalize();
+ vector = Vector3.Cross(vector3, vector4);
+ vector.Normalize();
+ }
+
+ result.m00 = vector3.mX;
+ result.m10 = vector3.mY;
+ result.m20 = vector3.mZ;
+ result.m30 = 0;
+ result.m01 = vector4.mX;
+ result.m11 = vector4.mY;
+ result.m21 = vector4.mZ;
+ result.m31 = 0;
+ result.m02 = vector.mX;
+ result.m12 = vector.mY;
+ result.m22 = vector.mZ;
+ result.m32 = 0;
+ result.m03 = objectPosition.mX;
+ result.m13 = objectPosition.mY;
+ result.m23 = objectPosition.mZ;
+ result.m33 = 1;
+ }
+
+ ///
+ /// Creates a new matrix which contains the rotation moment around specified axis.
+ ///
+ /// The axis of rotation.
+ /// The angle of rotation in radians.
+ /// The rotation matrix.
+ public static Matrix4 CreateFromAxisAngle(Vector3 axis, float angle)
+ {
+ Matrix4 result;
+ CreateFromAxisAngle(axis, angle, out result);
+ return result;
+ }
+
+ ///
+ /// Creates a new matrix which contains the rotation moment around specified axis.
+ ///
+ /// The axis of rotation.
+ /// The angle of rotation in radians.
+ /// The rotation matrix as an output parameter.
+ public static void CreateFromAxisAngle(
+ Vector3 axis,
+ float angle,
+ out Matrix4 result
+ ) {
+ float x = axis.mX;
+ float y = axis.mY;
+ float z = axis.mZ;
+ float num2 = (float) Math.Sin((double) angle);
+ float num = (float) Math.Cos((double) angle);
+ float num11 = x * x;
+ float num10 = y * y;
+ float num9 = z * z;
+ float num8 = x * y;
+ float num7 = x * z;
+ float num6 = y * z;
+ result.m00 = num11 + (num * (1f - num11));
+ result.m10 = (num8 - (num * num8)) + (num2 * z);
+ result.m20 = (num7 - (num * num7)) - (num2 * y);
+ result.m30 = 0;
+ result.m01 = (num8 - (num * num8)) - (num2 * z);
+ result.m11 = num10 + (num * (1f - num10));
+ result.m21 = (num6 - (num * num6)) + (num2 * x);
+ result.m31 = 0;
+ result.m02 = (num7 - (num * num7)) + (num2 * y);
+ result.m12 = (num6 - (num * num6)) - (num2 * x);
+ result.m22 = num9 + (num * (1f - num9));
+ result.m32 = 0;
+ result.m03 = 0;
+ result.m13 = 0;
+ result.m23 = 0;
+ result.m33 = 1;
+ }
+
+ ///
+ /// Creates a new rotation matrix from a .
+ ///
+ /// of rotation moment.
+ /// The rotation matrix.
+ public static Matrix4 CreateFromQuaternion(Quaternion quaternion)
+ {
+ Matrix4 result;
+ CreateFromQuaternion(quaternion, out result);
+ return result;
+ }
+
+ ///
+ /// Creates a new rotation matrix from a .
+ ///
+ /// of rotation moment.
+ /// The rotation matrix as an output parameter.
+ public static void CreateFromQuaternion(Quaternion quaternion, out Matrix4 result)
+ {
+ float num9 = quaternion.mX * quaternion.mX;
+ float num8 = quaternion.mY * quaternion.mY;
+ float num7 = quaternion.mZ * quaternion.mZ;
+ float num6 = quaternion.mX * quaternion.mY;
+ float num5 = quaternion.mZ * quaternion.mW;
+ float num4 = quaternion.mZ * quaternion.mX;
+ float num3 = quaternion.mY * quaternion.mW;
+ float num2 = quaternion.mY * quaternion.mZ;
+ float num = quaternion.mX * quaternion.mW;
+ result.m00 = 1f - (2f * (num8 + num7));
+ result.m10 = 2f * (num6 + num5);
+ result.m20 = 2f * (num4 - num3);
+ result.m30 = 0f;
+ result.m01 = 2f * (num6 - num5);
+ result.m11 = 1f - (2f * (num7 + num9));
+ result.m21 = 2f * (num2 + num);
+ result.m31 = 0f;
+ result.m02 = 2f * (num4 + num3);
+ result.m12 = 2f * (num2 - num);
+ result.m22 = 1f - (2f * (num8 + num9));
+ result.m32 = 0f;
+ result.m03 = 0f;
+ result.m13 = 0f;
+ result.m23 = 0f;
+ result.m33 = 1f;
+ }
+
+ /// Creates a new rotation matrix from the specified yaw, pitch and roll values.
+ /// @param yaw The yaw rotation value in radians.
+ /// @param pitch The pitch rotation value in radians.
+ /// @param roll The roll rotation value in radians.
+ /// @returns The rotation matrix
+ /// @remarks For more information about yaw, pitch and roll visit http://en.wikipedia.org/wiki/Euler_angles.
+ public static Matrix4 CreateFromYawPitchRoll(float yaw, float pitch, float roll)
+ {
+ Matrix4 matrix;
+ CreateFromYawPitchRoll(yaw, pitch, roll, out matrix);
+ return matrix;
+ }
+
+ /// Creates a new rotation matrix from the specified yaw, pitch and roll values.
+ /// @param yaw The yaw rotation value in radians.
+ /// @param pitch The pitch rotation value in radians.
+ /// @param roll The roll rotation value in radians.
+ /// @param result The rotation matrix as an output parameter.
+ /// @remarks>For more information about yaw, pitch and roll visit http://en.wikipedia.org/wiki/Euler_angles.
+ public static void CreateFromYawPitchRoll(
+ float yaw,
+ float pitch,
+ float roll,
+ out Matrix4 result
+ ) {
+ Quaternion quaternion;
+ Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll, out quaternion);
+ CreateFromQuaternion(quaternion, out result);
+ }
+
+ /// Creates a new viewing matrix.
+ /// @param cameraPosition Position of the camera.
+ /// @param cameraTarget Lookup vector of the camera.
+ /// @param cameraUpVector The direction of the upper edge of the camera.
+ /// @returns The viewing matrix.
+ public static Matrix4 CreateLookAt(
+ Vector3 cameraPosition,
+ Vector3 cameraTarget,
+ Vector3 cameraUpVector
+ ) {
+ Matrix4 matrix;
+ CreateLookAt(cameraPosition, cameraTarget, cameraUpVector, out matrix);
+ return matrix;
+ }
+
+ /// Creates a new viewing matrix.
+ /// @param cameraPosition Position of the camera.
+ /// @param cameraTarget Lookup vector of the camera.
+ /// @param cameraUpVector The direction of the upper edge of the camera.
+ /// @param result The viewing matrix as an output parameter.
+ public static void CreateLookAt(
+ Vector3 cameraPosition,
+ Vector3 cameraTarget,
+ Vector3 cameraUpVector,
+ out Matrix4 result
+ ) {
+ Vector3 vectorA = Vector3.Normalize(cameraPosition - cameraTarget);
+ Vector3 vectorB = Vector3.Normalize(Vector3.Cross(cameraUpVector, vectorA));
+ Vector3 vectorC = Vector3.Cross(vectorA, vectorB);
+ result.m00 = vectorB.mX;
+ result.m10 = vectorC.mX;
+ result.m20 = vectorA.mX;
+ result.m30 = 0f;
+ result.m01 = vectorB.mY;
+ result.m11 = vectorC.mY;
+ result.m21 = vectorA.mY;
+ result.m31 = 0f;
+ result.m02 = vectorB.mZ;
+ result.m12 = vectorC.mZ;
+ result.m22 = vectorA.mZ;
+ result.m32 = 0f;
+ result.m03 = -Vector3.Dot(vectorB, cameraPosition);
+ result.m13 = -Vector3.Dot(vectorC, cameraPosition);
+ result.m23 = -Vector3.Dot(vectorA, cameraPosition);
+ result.m33 = 1f;
+ }
+
+ /// Creates a new projection matrix for orthographic view.
+ /// @param width Width of the viewing volume.
+ /// @param height Height of the viewing volume.
+ /// @param zNearPlane Depth of the near plane.
+ /// @param zFarPlane Depth of the far plane.
+ /// @returns The new projection matrix for orthographic view.
+ public static Matrix4 CreateOrthographic(
+ float width,
+ float height,
+ float zNearPlane,
+ float zFarPlane
+ ) {
+ Matrix4 matrix;
+ CreateOrthographic(width, height, zNearPlane, zFarPlane, out matrix);
+ return matrix;
+ }
+
+ /// Creates a new projection matrix for orthographic view.
+ /// @param width Width of the viewing volume.
+ /// @param height Height of the viewing volume.
+ /// @param zNearPlane Depth of the near plane.
+ /// @param zFarPlane Depth of the far plane.
+ /// @param result The new projection matrix for orthographic view as an output parameter.
+ public static void CreateOrthographic(
+ float width,
+ float height,
+ float zNearPlane,
+ float zFarPlane,
+ out Matrix4 result
+ ) {
+ result.m00 = 2f / width;
+ result.m10 = result.m20 = result.m30 = 0f;
+ result.m11 = 2f / height;
+ result.m01 = result.m21 = result.m31 = 0f;
+ result.m22 = 1f / (zNearPlane - zFarPlane);
+ result.m02 = result.m12 = result.m32 = 0f;
+ result.m03 = result.m13 = 0f;
+ result.m23 = zNearPlane / (zNearPlane - zFarPlane);
+ result.m33 = 1f;
+ }
+
+ /// Creates a new projection matrix for customized orthographic view.
+ /// @param left Lower x-value at the near plane.
+ /// @param right Upper x-value at the near plane.
+ /// @param bottom Lower y-coordinate at the near plane.
+ /// @param top Upper y-value at the near plane.
+ /// @param zNearPlane Depth of the near plane.
+ /// @param zFarPlane Depth of the far plane.
+ /// @returns The new projection matrix for customized orthographic view.
+ public static Matrix4 CreateOrthographicOffCenter(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float zNearPlane,
+ float zFarPlane
+ ) {
+ Matrix4 matrix;
+ CreateOrthographicOffCenter(
+ left,
+ right,
+ bottom,
+ top,
+ zNearPlane,
+ zFarPlane,
+ out matrix
+ );
+ return matrix;
+ }
+
+ /// Creates a new projection matrix for customized orthographic view.
+ /// @param left Lower x-value at the near plane.
+ /// @param right Upper x-value at the near plane.
+ /// @param bottom Lower y-coordinate at the near plane.
+ /// @param top Upper y-value at the near plane.
+ /// @param zNearPlane Depth of the near plane.
+ /// @param zFarPlane Depth of the far plane.
+ /// @param result The new projection matrix for customized orthographic view as an output parameter.
+ public static void CreateOrthographicOffCenter(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float zNearPlane,
+ float zFarPlane,
+ out Matrix4 result
+ )
+ {
+ result.m00 = (float) (2.0 / ((double) right - (double) left));
+ result.m10 = 0.0f;
+ result.m20 = 0.0f;
+ result.m30 = 0.0f;
+ result.m01 = 0.0f;
+ result.m11 = (float) (2.0 / ((double) top - (double) bottom));
+ result.m21 = 0.0f;
+ result.m31 = 0.0f;
+ result.m02 = 0.0f;
+ result.m12 = 0.0f;
+ result.m22 = (float) (1.0 / ((double) zNearPlane - (double) zFarPlane));
+ result.m32 = 0.0f;
+ result.m03 = (float) (
+ ((double) left + (double) right) /
+ ((double) left - (double) right)
+ );
+ result.m13 = (float) (
+ ((double) top + (double) bottom) /
+ ((double) bottom - (double) top)
+ );
+ result.m23 = (float) (
+ (double) zNearPlane /
+ ((double) zNearPlane - (double) zFarPlane)
+ );
+ result.m33 = 1.0f;
+ }
+
+ /// Creates a new matrix that flattens geometry into a specified as if casting a shadow from a specified light source.
+ /// @param lightDirection A vector specifying the direction from which the light that will cast the shadow is coming.
+ /// @param plane The plane onto which the new matrix should flatten geometry so as to cast a shadow.
+ /// @returns>A matrix that can be used to flatten geometry onto the specified plane from the specified direction.
+ public static Matrix4 CreateShadow(Vector3 lightDirection, Plane plane)
+ {
+ Matrix4 result;
+ result = CreateShadow(lightDirection, plane);
+ return result;
+ }
+
+ /// Creates a new matrix that flattens geometry into a specified as if casting a shadow from a specified light source.
+ /// @param lightDirection A vector specifying the direction from which the light that will cast the shadow is coming.
+ /// @param plane The plane onto which the new matrix should flatten geometry so as to cast a shadow.
+ /// @param result A matrix that can be used to flatten geometry onto the specified plane from the specified direction as an output parameter.
+ public static void CreateShadow(
+ Vector3 lightDirection,
+ Plane plane,
+ out Matrix4 result)
+ {
+ float dot = (
+ (plane.Normal.mX * lightDirection.mX) +
+ (plane.Normal.mY * lightDirection.mY) +
+ (plane.Normal.mZ * lightDirection.mZ)
+ );
+ float x = -plane.Normal.mX;
+ float y = -plane.Normal.mY;
+ float z = -plane.Normal.mZ;
+ float d = -plane.D;
+
+ result.m00 = (x * lightDirection.mX) + dot;
+ result.m10 = x * lightDirection.mY;
+ result.m20 = x * lightDirection.mZ;
+ result.m30 = 0;
+ result.m01 = y * lightDirection.mX;
+ result.m11 = (y * lightDirection.mY) + dot;
+ result.m21 = y * lightDirection.mZ;
+ result.m31 = 0;
+ result.m02 = z * lightDirection.mX;
+ result.m12 = z * lightDirection.mY;
+ result.m22 = (z * lightDirection.mZ) + dot;
+ result.m32 = 0;
+ result.m03 = d * lightDirection.mX;
+ result.m13 = d * lightDirection.mY;
+ result.m23 = d * lightDirection.mZ;
+ result.m33 = dot;
+ }
+
+ public override void ToString(System.String strBuffer)
+ {
+ for (int row < 4)
+ for (int col < 4)
+ {
+#unwarn
+ strBuffer.AppendF($"M{row+1}{col+1}:{((float*)&this)[row+col*4]}\n");
+ }
+ }
}
}
diff --git a/BeefLibs/Beefy2D/src/geom/Plane.bf b/BeefLibs/Beefy2D/src/geom/Plane.bf
new file mode 100644
index 00000000..8e52b3d7
--- /dev/null
+++ b/BeefLibs/Beefy2D/src/geom/Plane.bf
@@ -0,0 +1,291 @@
+// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA),
+// released under the Microsoft Public License
+
+using System;
+
+namespace Beefy.geom
+{
+ ///
+ /// Defines the intersection between a and a bounding volume.
+ ///
+ public enum PlaneIntersectionType
+ {
+ ///
+ /// There is no intersection, the bounding volume is in the negative half space of the plane.
+ ///
+ Front,
+ ///
+ /// There is no intersection, the bounding volume is in the positive half space of the plane.
+ ///
+ Back,
+ ///
+ /// The plane is intersected.
+ ///
+ Intersecting
+ }
+
+ public struct Plane
+ {
+ public Vector3 Normal;
+ public float D;
+
+ public this(Vector4 value)
+ : this(Vector3(value.mX, value.mY, value.mZ), value.mW)
+ {
+ }
+
+ public this(Vector3 normal, float d)
+ {
+ Normal = normal;
+ D = d;
+ }
+
+ public this(Vector3 a, Vector3 b, Vector3 c)
+ {
+ Vector3 ab = b - a;
+ Vector3 ac = c - a;
+
+ Vector3 cross = Vector3.Cross(ab, ac);
+ Vector3.Normalize(cross, out Normal);
+ D = -(Vector3.Dot(Normal, a));
+ }
+
+ public this(float a, float b, float c, float d)
+ : this(Vector3(a, b, c), d)
+ {
+
+ }
+
+ public float Dot(Vector4 value)
+ {
+ return (
+ (this.Normal.mX * value.mX) +
+ (this.Normal.mY * value.mY) +
+ (this.Normal.mZ * value.mZ) +
+ (this.D * value.mW)
+ );
+ }
+
+ public void Dot(ref Vector4 value, out float result)
+ {
+ result = (
+ (this.Normal.mX * value.mX) +
+ (this.Normal.mY * value.mY) +
+ (this.Normal.mZ * value.mZ) +
+ (this.D * value.mW)
+ );
+ }
+
+ public float DotCoordinate(Vector3 value)
+ {
+ return (
+ (this.Normal.mX * value.mX) +
+ (this.Normal.mY * value.mY) +
+ (this.Normal.mZ * value.mZ) +
+ this.D
+ );
+ }
+
+ public void DotCoordinate(ref Vector3 value, out float result)
+ {
+ result = (
+ (this.Normal.mX * value.mX) +
+ (this.Normal.mY * value.mY) +
+ (this.Normal.mZ * value.mZ) +
+ this.D
+ );
+ }
+
+ public float DotNormal(Vector3 value)
+ {
+ return (
+ (this.Normal.mX * value.mX) +
+ (this.Normal.mY * value.mY) +
+ (this.Normal.mZ * value.mZ)
+ );
+ }
+
+ public void DotNormal(Vector3 value, out float result)
+ {
+ result = (
+ (this.Normal.mX * value.mX) +
+ (this.Normal.mY * value.mY) +
+ (this.Normal.mZ * value.mZ)
+ );
+ }
+
+ public void Normalize() mut
+ {
+ float length = Normal.Length;
+ float factor = 1.0f / length;
+ Normal = Vector3.Multiply(Normal, factor);
+ D = D * factor;
+ }
+
+ /*public PlaneIntersectionType Intersects(BoundingBox box)
+ {
+ return box.Intersects(this);
+ }
+
+ public void Intersects(ref BoundingBox box, out PlaneIntersectionType result)
+ {
+ box.Intersects(ref this, out result);
+ }
+
+ public PlaneIntersectionType Intersects(BoundingSphere sphere)
+ {
+ return sphere.Intersects(this);
+ }
+
+ public void Intersects(ref BoundingSphere sphere, out PlaneIntersectionType result)
+ {
+ sphere.Intersects(ref this, out result);
+ }
+
+ public PlaneIntersectionType Intersects(BoundingFrustum frustum)
+ {
+ return frustum.Intersects(this);
+ }*/
+
+ #endregion
+
+ #region Internal Methods
+
+ internal PlaneIntersectionType Intersects(ref Vector3 point)
+ {
+ float distance;
+ DotCoordinate(ref point, out distance);
+ if (distance > 0)
+ {
+ return PlaneIntersectionType.Front;
+ }
+ if (distance < 0)
+ {
+ return PlaneIntersectionType.Back;
+ }
+ return PlaneIntersectionType.Intersecting;
+ }
+
+ #endregion
+
+ #region Public Static Methods
+
+ public static Plane Normalize(Plane value)
+ {
+ Plane ret;
+ Normalize(value, out ret);
+ return ret;
+ }
+
+ public static void Normalize(Plane value, out Plane result)
+ {
+ float length = value.Normal.Length;
+ float factor = 1.0f / length;
+ result.Normal = Vector3.Multiply(value.Normal, factor);
+ result.D = value.D * factor;
+ }
+
+ ///
+ /// Transforms a normalized plane by a matrix.
+ ///
+ /// The normalized plane to transform.
+ /// The transformation matrix.
+ /// The transformed plane.
+ public static Plane Transform(Plane plane, Matrix4 matrix)
+ {
+ Plane result;
+ Transform(plane, matrix, out result);
+ return result;
+ }
+
+ ///
+ /// Transforms a normalized plane by a matrix.
+ ///
+ /// The normalized plane to transform.
+ /// The transformation matrix.
+ /// The transformed plane.
+ public static void Transform(
+ Plane plane,
+ Matrix4 matrix,
+ out Plane result
+ ) {
+ /* See "Transforming Normals" in
+ * http://www.glprogramming.com/red/appendixf.html
+ * for an explanation of how this works.
+ */
+ Matrix4 transformedMatrix;
+ transformedMatrix = Matrix4.Invert(matrix);
+ transformedMatrix = Matrix4.Transpose(transformedMatrix);
+ Vector4 vector = Vector4(plane.Normal, plane.D);
+ Vector4 transformedVector;
+ Vector4.Transform(
+ vector,
+ transformedMatrix,
+ out transformedVector
+ );
+ result = Plane(transformedVector);
+ }
+
+ ///
+ /// Transforms a normalized plane by a quaternion rotation.
+ ///
+ /// The normalized plane to transform.
+ /// The quaternion rotation.
+ /// The transformed plane.
+ public static Plane Transform(Plane plane, Quaternion rotation)
+ {
+ Plane result;
+ Transform(plane, rotation, out result);
+ return result;
+ }
+
+ ///
+ /// Transforms a normalized plane by a quaternion rotation.
+ ///
+ /// The normalized plane to transform.
+ /// The quaternion rotation.
+ /// The transformed plane.
+ public static void Transform(
+ Plane plane,
+ Quaternion rotation,
+ out Plane result
+ ) {
+ result.Normal = Vector3.Transform(
+ plane.Normal,
+ rotation
+ );
+ result.D = plane.D;
+ }
+
+ #endregion
+
+ #region Public Static Operators and Override Methods
+
+ public static bool operator !=(Plane plane1, Plane plane2)
+ {
+ return !plane1.Equals(plane2);
+ }
+
+ public static bool operator ==(Plane plane1, Plane plane2)
+ {
+ return plane1.Equals(plane2);
+ }
+
+ public bool Equals(Plane other)
+ {
+ return (Normal == other.Normal && D == other.D);
+ }
+
+ public int GetHashCode()
+ {
+ return Normal.GetHashCode() ^ D.GetHashCode();
+ }
+
+ public override void ToString(String str)
+ {
+ str.AppendF($"{Normal:{Normal} D:{D}}");
+ }
+
+ #endregion
+ }
+}
diff --git a/BeefLibs/Beefy2D/src/geom/Quaternion.bf b/BeefLibs/Beefy2D/src/geom/Quaternion.bf
index c5f5d5e2..e7b2c7b9 100644
--- a/BeefLibs/Beefy2D/src/geom/Quaternion.bf
+++ b/BeefLibs/Beefy2D/src/geom/Quaternion.bf
@@ -1,3 +1,6 @@
+// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA),
+// released under the Microsoft Public License
+
using System;
using Beefy.gfx;
@@ -189,43 +192,50 @@ namespace Beefy.geom
public static void CreateFromRotationMatrix(ref Matrix4 matrix, out Quaternion result)
{
- float num8 = (matrix.m11 + matrix.m22) + matrix.m33;
- if (num8 > 0f)
- {
- float num = (float)Math.Sqrt((double)(num8 + 1f));
- result.mW = num * 0.5f;
- num = 0.5f / num;
- result.mX = (matrix.m23 - matrix.m32) * num;
- result.mY = (matrix.m31 - matrix.m13) * num;
- result.mZ = (matrix.m12 - matrix.m21) * num;
- }
- else if ((matrix.m11 >= matrix.m22) && (matrix.m11 >= matrix.m33))
- {
- float num7 = (float)Math.Sqrt((double)(((1f + matrix.m11) - matrix.m22) - matrix.m33));
- float num4 = 0.5f / num7;
- result.mX = 0.5f * num7;
- result.mY = (matrix.m12 + matrix.m21) * num4;
- result.mZ = (matrix.m13 + matrix.m31) * num4;
- result.mW = (matrix.m23 - matrix.m32) * num4;
- }
- else if (matrix.m22 > matrix.m33)
- {
- float num6 = (float)Math.Sqrt((double)(((1f + matrix.m22) - matrix.m11) - matrix.m33));
- float num3 = 0.5f / num6;
- result.mX = (matrix.m21 + matrix.m12) * num3;
- result.mY = 0.5f * num6;
- result.mZ = (matrix.m32 + matrix.m23) * num3;
- result.mW = (matrix.m31 - matrix.m13) * num3;
- }
- else
- {
- float num5 = (float)Math.Sqrt((double)(((1f + matrix.m33) - matrix.m11) - matrix.m22));
- float num2 = 0.5f / num5;
- result.mX = (matrix.m31 + matrix.m13) * num2;
- result.mY = (matrix.m32 + matrix.m23) * num2;
- result.mZ = 0.5f * num5;
- result.mW = (matrix.m12 - matrix.m21) * num2;
- }
+ float sqrt;
+ float half;
+ float scale = matrix.m00 + matrix.m11 + matrix.m22;
+
+ if (scale > 0.0f)
+ {
+ sqrt = (float) Math.Sqrt(scale + 1.0f);
+ result.mW = sqrt * 0.5f;
+ sqrt = 0.5f / sqrt;
+
+ result.mX = (matrix.m21 - matrix.m12) * sqrt;
+ result.mY = (matrix.m02 - matrix.m20) * sqrt;
+ result.mZ = (matrix.m10 - matrix.m01) * sqrt;
+ }
+ else if ((matrix.m00 >= matrix.m11) && (matrix.m00 >= matrix.m22))
+ {
+ sqrt = (float) Math.Sqrt(1.0f + matrix.m00 - matrix.m11 - matrix.m22);
+ half = 0.5f / sqrt;
+
+ result.mX = 0.5f * sqrt;
+ result.mY = (matrix.m10 + matrix.m01) * half;
+ result.mZ = (matrix.m20 + matrix.m02) * half;
+ result.mW = (matrix.m21 - matrix.m12) * half;
+ }
+ else if (matrix.m11 > matrix.m22)
+ {
+ sqrt = (float) Math.Sqrt(1.0f + matrix.m11 - matrix.m00 - matrix.m22);
+ half = 0.5f/sqrt;
+
+ result.mX = (matrix.m01 + matrix.m10)*half;
+ result.mY = 0.5f*sqrt;
+ result.mZ = (matrix.m12 + matrix.m21)*half;
+ result.mW = (matrix.m02 - matrix.m20)*half;
+ }
+ else
+ {
+ sqrt = (float) Math.Sqrt(1.0f + matrix.m22 - matrix.m00 - matrix.m11);
+ half = 0.5f / sqrt;
+
+ result.mX = (matrix.m02 + matrix.m20) * half;
+ result.mY = (matrix.m12 + matrix.m21) * half;
+ result.mZ = 0.5f * sqrt;
+ result.mW = (matrix.m10 - matrix.m01) * half;
+ }
}
public static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll)
diff --git a/BeefLibs/Beefy2D/src/geom/Vector3.bf b/BeefLibs/Beefy2D/src/geom/Vector3.bf
index be89f716..4ee85123 100644
--- a/BeefLibs/Beefy2D/src/geom/Vector3.bf
+++ b/BeefLibs/Beefy2D/src/geom/Vector3.bf
@@ -1,3 +1,6 @@
+// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA),
+// released under the Microsoft Public License
+
using System;
using System.Collections;
using System.Text;
@@ -131,7 +134,7 @@ namespace Beefy.geom
{
Vector3 newVec;
Normalize(vector, out newVec);
- return vector;
+ return newVec;
}
public static void Normalize(Vector3 value, out Vector3 result)
@@ -173,7 +176,7 @@ namespace Beefy.geom
return new Vector2D((float)Math.Cos(angle) * length, (float)Math.Sin(angle) * length);
}*/
- public static Vector3 Transform(Vector3 vec, Matrix4 matrix)
+ public static Vector3 TransformW(Vector3 vec, Matrix4 matrix)
{
Vector3 result;
float fInvW = 1.0f / (matrix.m30 * vec.mX + matrix.m31 * vec.mY + matrix.m32 * vec.mZ + matrix.m33);
@@ -181,9 +184,19 @@ namespace Beefy.geom
result.mX = (matrix.m00 * vec.mX + matrix.m01 * vec.mY + matrix.m02 * vec.mZ + matrix.m03) * fInvW;
result.mY = (matrix.m10 * vec.mX + matrix.m11 * vec.mY + matrix.m12 * vec.mZ + matrix.m13) * fInvW;
result.mZ = (matrix.m20 * vec.mX + matrix.m21 * vec.mY + matrix.m22 * vec.mZ + matrix.m23) * fInvW;
+
return result;
}
+ public static Vector3 Transform(Vector3 vec, Matrix4 matrix)
+ {
+ Vector3 result;
+ result.mX = (vec.mX * matrix.m00) + (vec.mY * matrix.m01) + (vec.mZ * matrix.m02) + matrix.m03;
+ result.mY = (vec.mX * matrix.m10) + (vec.mY * matrix.m11) + (vec.mZ * matrix.m12) + matrix.m13;
+ result.mZ = (vec.mX * matrix.m20) + (vec.mY * matrix.m21) + (vec.mZ * matrix.m22) + matrix.m23;
+ return result;
+ }
+
/*public static void Transform(Vector3[] sourceArray, ref Matrix4 matrix, Vector3[] destinationArray)
{
//Debug.Assert(destinationArray.Length >= sourceArray.Length, "The destination array is smaller than the source array.");
@@ -199,6 +212,51 @@ namespace Beefy.geom
}
}*/
+ ///
+ /// Returns a Vector3 pointing in the opposite
+ /// direction of .
+ ///
+ /// The vector to negate.
+ /// The vector negation of .
+ public static Vector3 Negate(Vector3 value)
+ {
+ return .(-value.mX, -value.mY, -value.mZ);
+ }
+
+ ///
+ /// Stores a Vector3 pointing in the opposite
+ /// direction of in .
+ ///
+ /// The vector to negate.
+ /// The vector that the negation of will be stored in.
+ public static void Negate(Vector3 value, out Vector3 result)
+ {
+ result.mX = -value.mX;
+ result.mY = -value.mY;
+ result.mZ = -value.mZ;
+ }
+
+ ///
+ /// Creates a new that contains a multiplication of two vectors.
+ ///
+ /// Source .
+ /// Source .
+ /// The result of the vector multiplication.
+ public static Vector3 Multiply(Vector3 value1, Vector3 value2)
+ {
+ return .(value1.mX * value2.mX, value1.mY * value2.mY, value1.mZ * value2.mZ);
+ }
+
+ public static Vector3 Multiply(Vector3 value1, float value2)
+ {
+ return .(value1.mX * value2, value1.mY * value2, value1.mZ * value2);
+ }
+
+ public void Normalize() mut
+ {
+ Normalize(this, out this);
+ }
+
public static Vector3 Transform(Vector3 vec, Quaternion quat)
{
Matrix4 matrix = quat.ToMatrix();
@@ -234,6 +292,11 @@ namespace Beefy.geom
return Vector3(vec1.mX - vec2.mX, vec1.mY - vec2.mY, vec1.mZ - vec2.mZ);
}
+ public static Vector3 operator -(Vector3 vec1)
+ {
+ return Vector3(-vec1.mX, -vec1.mY, -vec1.mZ);
+ }
+
public static Vector3 operator *(Vector3 vec, float scale)
{
return Vector3(vec.mX * scale, vec.mY * scale, vec.mZ * scale);
diff --git a/BeefLibs/Beefy2D/src/geom/Vector4.bf b/BeefLibs/Beefy2D/src/geom/Vector4.bf
new file mode 100644
index 00000000..9f15bc6f
--- /dev/null
+++ b/BeefLibs/Beefy2D/src/geom/Vector4.bf
@@ -0,0 +1,1435 @@
+// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA),
+// released under the Microsoft Public License
+
+using System;
+
+namespace Beefy.geom
+{
+ public struct Vector4 : IHashable
+ {
+ #region Public Static Properties
+
+ ///
+ /// Returns a with components 0, 0, 0, 0.
+ ///
+ public static Vector4 Zero
+ {
+ get
+ {
+ return zero;
+ }
+ }
+
+ ///
+ /// Returns a with components 1, 1, 1, 1.
+ ///
+ public static Vector4 One
+ {
+ get
+ {
+ return unit;
+ }
+ }
+
+ ///
+ /// Returns a with components 1, 0, 0, 0.
+ ///
+ public static Vector4 UnitX
+ {
+ get
+ {
+ return unitX;
+ }
+ }
+
+ ///
+ /// Returns a with components 0, 1, 0, 0.
+ ///
+ public static Vector4 UnitY
+ {
+ get
+ {
+ return unitY;
+ }
+ }
+
+ ///
+ /// Returns a with components 0, 0, 1, 0.
+ ///
+ public static Vector4 UnitZ
+ {
+ get
+ {
+ return unitZ;
+ }
+ }
+
+ ///
+ /// Returns a with components 0, 0, 0, 1.
+ ///
+ public static Vector4 UnitW
+ {
+ get
+ {
+ return unitW;
+ }
+ }
+
+ ///
+ /// The x coordinate of this .
+ ///
+ public float mX;
+
+ ///
+ /// The y coordinate of this .
+ ///
+ public float mY;
+
+ ///
+ /// The z coordinate of this .
+ ///
+ public float mZ;
+
+ ///
+ /// The w coordinate of this .
+ ///
+ public float mW;
+
+ #endregion
+
+ #region Private Static Fields
+
+ private static Vector4 zero = .(); // Not readonly for performance -flibit
+ private static readonly Vector4 unit = .(1f, 1f, 1f, 1f);
+ private static readonly Vector4 unitX = .(1f, 0f, 0f, 0f);
+ private static readonly Vector4 unitY = .(0f, 1f, 0f, 0f);
+ private static readonly Vector4 unitZ = .(0f, 0f, 1f, 0f);
+ private static readonly Vector4 unitW = .(0f, 0f, 0f, 1f);
+
+ #endregion
+
+ #region Public Constructors
+
+ public this()
+ {
+ mX = 0;
+ mY = 0;
+ mZ = 0;
+ mW = 0;
+ }
+
+ ///
+ /// Constructs a 3d vector with X, Y, Z and W from four values.
+ ///
+ /// The x coordinate in 4d-space.
+ /// The y coordinate in 4d-space.
+ /// The z coordinate in 4d-space.
+ /// The w coordinate in 4d-space.
+ public this(float x, float y, float z, float w)
+ {
+ this.mX = x;
+ this.mY = y;
+ this.mZ = z;
+ this.mW = w;
+ }
+
+ ///
+ /// Constructs a 3d vector with X and Z from and Z and W from the scalars.
+ ///
+ /// The x and y coordinates in 4d-space.
+ /// The z coordinate in 4d-space.
+ /// The w coordinate in 4d-space.
+ public this(Vector2 value, float z, float w)
+ {
+ this.mX = value.mX;
+ this.mY = value.mY;
+ this.mZ = z;
+ this.mW = w;
+ }
+
+ ///
+ /// Constructs a 3d vector with X, Y, Z from and W from a scalar.
+ ///
+ /// The x, y and z coordinates in 4d-space.
+ /// The w coordinate in 4d-space.
+ public this(Vector3 value, float w)
+ {
+ this.mX = value.mX;
+ this.mY = value.mY;
+ this.mZ = value.mZ;
+ this.mW = w;
+ }
+
+ ///
+ /// Constructs a 4d vector with X, Y, Z and W set to the same value.
+ ///
+ /// The x, y, z and w coordinates in 4d-space.
+ public this(float value)
+ {
+ this.mX = value;
+ this.mY = value;
+ this.mZ = value;
+ this.mW = value;
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Compares whether current instance is equal to specified .
+ ///
+ /// The to compare.
+ /// true if the instances are equal; false otherwise.
+ public bool Equals(Vector4 other)
+ {
+ return ( mX == other.mX &&
+ mY == other.mY &&
+ mZ == other.mZ &&
+ mW == other.mW );
+ }
+
+ ///
+ /// Gets the hash code of this .
+ ///
+ /// Hash code of this .
+ public int GetHashCode()
+ {
+ return mW.GetHashCode() + mX.GetHashCode() + mY.GetHashCode() + mZ.GetHashCode();
+ }
+
+ ///
+ /// Returns the length of this .
+ ///
+ /// The length of this .
+ public float Length()
+ {
+ return (float) Math.Sqrt((mX * mX) + (mY * mY) + (mZ * mZ) + (mW * mW));
+ }
+
+ ///
+ /// Returns the squared length of this .
+ ///
+ /// The squared length of this .
+ public float LengthSquared()
+ {
+ return (mX * mX) + (mY * mY) + (mZ * mZ) + (mW * mW);
+ }
+
+ ///
+ /// Turns this to a unit vector with the same direction.
+ ///
+ public void Normalize() mut
+ {
+ float factor = 1.0f / (float) Math.Sqrt(
+ (mX * mX) +
+ (mY * mY) +
+ (mZ * mZ) +
+ (mW * mW)
+ );
+ mX *= factor;
+ mY *= factor;
+ mZ *= factor;
+ mW *= factor;
+ }
+
+ public override void ToString(String str)
+ {
+ str.AppendF($"({mX}, {mY}, {mZ}, {mW})");
+ }
+
+ ///
+ /// Performs vector addition on and .
+ ///
+ /// The first vector to add.
+ /// The second vector to add.
+ /// The result of the vector addition.
+ public static Vector4 Add(Vector4 value1, Vector4 value2)
+ {
+ return .(
+ value1.mX + value2.mX,
+ value1.mY + value2.mY,
+ value1.mZ + value2.mZ,
+ value1.mW + value2.mW);
+ }
+
+ ///
+ /// Performs vector addition on and
+ /// , storing the result of the
+ /// addition in .
+ ///
+ /// The first vector to add.
+ /// The second vector to add.
+ /// The result of the vector addition.
+ public static void Add(ref Vector4 value1, ref Vector4 value2, out Vector4 result)
+ {
+ result.mW = value1.mW + value2.mW;
+ result.mX = value1.mX + value2.mX;
+ result.mY = value1.mY + value2.mY;
+ result.mZ = value1.mZ + value2.mZ;
+ }
+
+ ///
+ /// Creates a new that contains the cartesian coordinates of a vector specified in barycentric coordinates and relative to 4d-triangle.
+ ///
+ /// The first vector of 4d-triangle.
+ /// The second vector of 4d-triangle.
+ /// The third vector of 4d-triangle.
+ /// Barycentric scalar b2 which represents a weighting factor towards second vector of 4d-triangle.
+ /// Barycentric scalar b3 which represents a weighting factor towards third vector of 4d-triangle.
+ /// The cartesian translation of barycentric coordinates.
+ public static Vector4 Barycentric(
+ Vector4 value1,
+ Vector4 value2,
+ Vector4 value3,
+ float amount1,
+ float amount2
+ ) {
+ return Vector4(
+ Math.Barycentric(value1.mX, value2.mX, value3.mX, amount1, amount2),
+ Math.Barycentric(value1.mY, value2.mY, value3.mY, amount1, amount2),
+ Math.Barycentric(value1.mZ, value2.mZ, value3.mZ, amount1, amount2),
+ Math.Barycentric(value1.mW, value2.mW, value3.mW, amount1, amount2)
+ );
+ }
+
+ ///
+ /// Creates a new that contains the cartesian coordinates of a vector specified in barycentric coordinates and relative to 4d-triangle.
+ ///
+ /// The first vector of 4d-triangle.
+ /// The second vector of 4d-triangle.
+ /// The third vector of 4d-triangle.
+ /// Barycentric scalar b2 which represents a weighting factor towards second vector of 4d-triangle.
+ /// Barycentric scalar b3 which represents a weighting factor towards third vector of 4d-triangle.
+ /// The cartesian translation of barycentric coordinates as an output parameter.
+ public static void Barycentric(
+ ref Vector4 value1,
+ ref Vector4 value2,
+ ref Vector4 value3,
+ float amount1,
+ float amount2,
+ out Vector4 result
+ ) {
+ result.mX = Math.Barycentric(value1.mX, value2.mX, value3.mX, amount1, amount2);
+ result.mY = Math.Barycentric(value1.mY, value2.mY, value3.mY, amount1, amount2);
+ result.mZ = Math.Barycentric(value1.mZ, value2.mZ, value3.mZ, amount1, amount2);
+ result.mW = Math.Barycentric(value1.mW, value2.mW, value3.mW, amount1, amount2);
+ }
+
+ ///
+ /// Creates a new that contains CatmullRom interpolation of the specified vectors.
+ ///
+ /// The first vector in interpolation.
+ /// The second vector in interpolation.
+ /// The third vector in interpolation.
+ /// The fourth vector in interpolation.
+ /// Weighting factor.
+ /// The result of CatmullRom interpolation.
+ public static Vector4 CatmullRom(
+ Vector4 value1,
+ Vector4 value2,
+ Vector4 value3,
+ Vector4 value4,
+ float amount
+ ) {
+ return Vector4(
+ Math.CatmullRom(value1.mX, value2.mX, value3.mX, value4.mX, amount),
+ Math.CatmullRom(value1.mY, value2.mY, value3.mY, value4.mY, amount),
+ Math.CatmullRom(value1.mZ, value2.mZ, value3.mZ, value4.mZ, amount),
+ Math.CatmullRom(value1.mW, value2.mW, value3.mW, value4.mW, amount)
+ );
+ }
+
+ ///
+ /// Creates a new that contains CatmullRom interpolation of the specified vectors.
+ ///
+ /// The first vector in interpolation.
+ /// The second vector in interpolation.
+ /// The third vector in interpolation.
+ /// The fourth vector in interpolation.
+ /// Weighting factor.
+ /// The result of CatmullRom interpolation as an output parameter.
+ public static void CatmullRom(
+ ref Vector4 value1,
+ ref Vector4 value2,
+ ref Vector4 value3,
+ ref Vector4 value4,
+ float amount,
+ out Vector4 result
+ ) {
+ result.mX = Math.CatmullRom(value1.mX, value2.mX, value3.mX, value4.mX, amount);
+ result.mY = Math.CatmullRom(value1.mY, value2.mY, value3.mY, value4.mY, amount);
+ result.mZ = Math.CatmullRom(value1.mZ, value2.mZ, value3.mZ, value4.mZ, amount);
+ result.mW = Math.CatmullRom(value1.mW, value2.mW, value3.mW, value4.mW, amount);
+ }
+
+ ///
+ /// Clamps the specified value within a range.
+ ///
+ /// The value to clamp.
+ /// The min value.
+ /// The max value.
+ /// The clamped value.
+ public static Vector4 Clamp(Vector4 value1, Vector4 min, Vector4 max)
+ {
+ return Vector4(
+ Math.Clamp(value1.mX, min.mX, max.mX),
+ Math.Clamp(value1.mY, min.mY, max.mY),
+ Math.Clamp(value1.mZ, min.mZ, max.mZ),
+ Math.Clamp(value1.mW, min.mW, max.mW)
+ );
+ }
+
+ ///
+ /// Clamps the specified value within a range.
+ ///
+ /// The value to clamp.
+ /// The min value.
+ /// The max value.
+ /// The clamped value as an output parameter.
+ public static void Clamp(
+ Vector4 value1,
+ Vector4 min,
+ Vector4 max,
+ out Vector4 result
+ ) {
+ result.mX = Math.Clamp(value1.mX, min.mX, max.mX);
+ result.mY = Math.Clamp(value1.mY, min.mY, max.mY);
+ result.mZ = Math.Clamp(value1.mZ, min.mZ, max.mZ);
+ result.mW = Math.Clamp(value1.mW, min.mW, max.mW);
+ }
+
+ ///
+ /// Returns the distance between two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The distance between two vectors.
+ public static float Distance(Vector4 value1, Vector4 value2)
+ {
+ return (float) Math.Sqrt(DistanceSquared(value1, value2));
+ }
+
+ ///
+ /// Returns the distance between two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The distance between two vectors as an output parameter.
+ public static void Distance(ref Vector4 value1, ref Vector4 value2, out float result)
+ {
+ result = (float) Math.Sqrt(DistanceSquared(value1, value2));
+ }
+
+ ///
+ /// Returns the squared distance between two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The squared distance between two vectors.
+ public static float DistanceSquared(Vector4 value1, Vector4 value2)
+ {
+ return (
+ (value1.mW - value2.mW) * (value1.mW - value2.mW) +
+ (value1.mX - value2.mX) * (value1.mX - value2.mX) +
+ (value1.mY - value2.mY) * (value1.mY - value2.mY) +
+ (value1.mZ - value2.mZ) * (value1.mZ - value2.mZ)
+ );
+ }
+
+ ///
+ /// Returns the squared distance between two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The squared distance between two vectors as an output parameter.
+ public static void DistanceSquared(
+ ref Vector4 value1,
+ ref Vector4 value2,
+ out float result
+ ) {
+ result = (
+ (value1.mW - value2.mW) * (value1.mW - value2.mW) +
+ (value1.mX - value2.mX) * (value1.mX - value2.mX) +
+ (value1.mY - value2.mY) * (value1.mY - value2.mY) +
+ (value1.mZ - value2.mZ) * (value1.mZ - value2.mZ)
+ );
+ }
+
+ ///
+ /// Divides the components of a by the components of another .
+ ///
+ /// Source .
+ /// Divisor .
+ /// The result of dividing the vectors.
+ public static Vector4 Divide(Vector4 value1, Vector4 value2)
+ {
+ var value1;
+ value1.mW /= value2.mW;
+ value1.mX /= value2.mX;
+ value1.mY /= value2.mY;
+ value1.mZ /= value2.mZ;
+ return value1;
+ }
+
+ ///
+ /// Divides the components of a by a scalar.
+ ///
+ /// Source .
+ /// Divisor scalar.
+ /// The result of dividing a vector by a scalar.
+ public static Vector4 Divide(Vector4 value1, float divider)
+ {
+ var value1;
+ float factor = 1f / divider;
+ value1.mW *= factor;
+ value1.mX *= factor;
+ value1.mY *= factor;
+ value1.mZ *= factor;
+ return value1;
+ }
+
+ ///
+ /// Divides the components of a by a scalar.
+ ///
+ /// Source .
+ /// Divisor scalar.
+ /// The result of dividing a vector by a scalar as an output parameter.
+ public static void Divide(ref Vector4 value1, float divider, out Vector4 result)
+ {
+ float factor = 1f / divider;
+ result.mW = value1.mW * factor;
+ result.mX = value1.mX * factor;
+ result.mY = value1.mY * factor;
+ result.mZ = value1.mZ * factor;
+ }
+
+ ///
+ /// Divides the components of a by the components of another .
+ ///
+ /// Source .
+ /// Divisor .
+ /// The result of dividing the vectors as an output parameter.
+ public static void Divide(
+ ref Vector4 value1,
+ ref Vector4 value2,
+ out Vector4 result
+ ) {
+ result.mW = value1.mW / value2.mW;
+ result.mX = value1.mX / value2.mX;
+ result.mY = value1.mY / value2.mY;
+ result.mZ = value1.mZ / value2.mZ;
+ }
+
+ ///
+ /// Returns a dot product of two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The dot product of two vectors.
+ public static float Dot(Vector4 vector1, Vector4 vector2)
+ {
+ return (
+ vector1.mX * vector2.mX +
+ vector1.mY * vector2.mY +
+ vector1.mZ * vector2.mZ +
+ vector1.mW * vector2.mW
+ );
+ }
+
+ ///
+ /// Returns a dot product of two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The dot product of two vectors as an output parameter.
+ public static void Dot(ref Vector4 vector1, ref Vector4 vector2, out float result)
+ {
+ result = (
+ (vector1.mX * vector2.mX) +
+ (vector1.mY * vector2.mY) +
+ (vector1.mZ * vector2.mZ) +
+ (vector1.mW * vector2.mW)
+ );
+ }
+
+ ///
+ /// Creates a new that contains hermite spline interpolation.
+ ///
+ /// The first position vector.
+ /// The first tangent vector.
+ /// The second position vector.
+ /// The second tangent vector.
+ /// Weighting factor.
+ /// The hermite spline interpolation vector.
+ public static Vector4 Hermite(
+ Vector4 value1,
+ Vector4 tangent1,
+ Vector4 value2,
+ Vector4 tangent2,
+ float amount
+ ) {
+ return Vector4(
+ Math.Hermite(value1.mX, tangent1.mX, value2.mX, tangent2.mX, amount),
+ Math.Hermite(value1.mY, tangent1.mY, value2.mY, tangent2.mY, amount),
+ Math.Hermite(value1.mZ, tangent1.mZ, value2.mZ, tangent2.mZ, amount),
+ Math.Hermite(value1.mW, tangent1.mW, value2.mW, tangent2.mW, amount)
+ );
+ }
+
+ ///
+ /// Creates a new that contains hermite spline interpolation.
+ ///
+ /// The first position vector.
+ /// The first tangent vector.
+ /// The second position vector.
+ /// The second tangent vector.
+ /// Weighting factor.
+ /// The hermite spline interpolation vector as an output parameter.
+ public static void Hermite(
+ Vector4 value1,
+ Vector4 tangent1,
+ Vector4 value2,
+ Vector4 tangent2,
+ float amount,
+ out Vector4 result
+ ) {
+ result.mW = Math.Hermite(value1.mW, tangent1.mW, value2.mW, tangent2.mW, amount);
+ result.mX = Math.Hermite(value1.mX, tangent1.mX, value2.mX, tangent2.mX, amount);
+ result.mY = Math.Hermite(value1.mY, tangent1.mY, value2.mY, tangent2.mY, amount);
+ result.mZ = Math.Hermite(value1.mZ, tangent1.mZ, value2.mZ, tangent2.mZ, amount);
+ }
+
+ ///
+ /// Creates a new that contains linear interpolation of the specified vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// Weighting value(between 0.0 and 1.0).
+ /// The result of linear interpolation of the specified vectors.
+ public static Vector4 Lerp(Vector4 value1, Vector4 value2, float amount)
+ {
+ return Vector4(
+ Math.Lerp(value1.mX, value2.mX, amount),
+ Math.Lerp(value1.mY, value2.mY, amount),
+ Math.Lerp(value1.mZ, value2.mZ, amount),
+ Math.Lerp(value1.mW, value2.mW, amount)
+ );
+ }
+
+ ///
+ /// Creates a new that contains linear interpolation of the specified vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// Weighting value(between 0.0 and 1.0).
+ /// The result of linear interpolation of the specified vectors as an output parameter.
+ public static void Lerp(
+ ref Vector4 value1,
+ ref Vector4 value2,
+ float amount,
+ out Vector4 result
+ ) {
+ result.mX = Math.Lerp(value1.mX, value2.mX, amount);
+ result.mY = Math.Lerp(value1.mY, value2.mY, amount);
+ result.mZ = Math.Lerp(value1.mZ, value2.mZ, amount);
+ result.mW = Math.Lerp(value1.mW, value2.mW, amount);
+ }
+
+ ///
+ /// Creates a new that contains a maximal values from the two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The with maximal values from the two vectors.
+ public static Vector4 Max(Vector4 value1, Vector4 value2)
+ {
+ return Vector4(
+ Math.Max(value1.mX, value2.mX),
+ Math.Max(value1.mY, value2.mY),
+ Math.Max(value1.mZ, value2.mZ),
+ Math.Max(value1.mW, value2.mW)
+ );
+ }
+
+ ///
+ /// Creates a new that contains a maximal values from the two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The with maximal values from the two vectors as an output parameter.
+ public static void Max(ref Vector4 value1, ref Vector4 value2, out Vector4 result)
+ {
+ result.mX = Math.Max(value1.mX, value2.mX);
+ result.mY = Math.Max(value1.mY, value2.mY);
+ result.mZ = Math.Max(value1.mZ, value2.mZ);
+ result.mW = Math.Max(value1.mW, value2.mW);
+ }
+
+ ///
+ /// Creates a new that contains a minimal values from the two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The with minimal values from the two vectors.
+ public static Vector4 Min(Vector4 value1, Vector4 value2)
+ {
+ return Vector4(
+ Math.Min(value1.mX, value2.mX),
+ Math.Min(value1.mY, value2.mY),
+ Math.Min(value1.mZ, value2.mZ),
+ Math.Min(value1.mW, value2.mW)
+ );
+ }
+
+ ///
+ /// Creates a new that contains a minimal values from the two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The with minimal values from the two vectors as an output parameter.
+ public static void Min(ref Vector4 value1, ref Vector4 value2, out Vector4 result)
+ {
+ result.mX = Math.Min(value1.mX, value2.mX);
+ result.mY = Math.Min(value1.mY, value2.mY);
+ result.mZ = Math.Min(value1.mZ, value2.mZ);
+ result.mW = Math.Min(value1.mW, value2.mW);
+ }
+
+ ///
+ /// Creates a new that contains a multiplication of two vectors.
+ ///
+ /// Source .
+ /// Source .
+ /// The result of the vector multiplication.
+ public static Vector4 Multiply(Vector4 value1, Vector4 value2)
+ {
+ var value1;
+ value1.mW *= value2.mW;
+ value1.mX *= value2.mX;
+ value1.mY *= value2.mY;
+ value1.mZ *= value2.mZ;
+ return value1;
+ }
+
+ ///
+ /// Creates a new that contains a multiplication of and a scalar.
+ ///
+ /// Source .
+ /// Scalar value.
+ /// The result of the vector multiplication with a scalar.
+ public static Vector4 Multiply(Vector4 value1, float scaleFactor)
+ {
+ var value1;
+ value1.mW *= scaleFactor;
+ value1.mX *= scaleFactor;
+ value1.mY *= scaleFactor;
+ value1.mZ *= scaleFactor;
+ return value1;
+ }
+
+ ///
+ /// Creates a new that contains a multiplication of and a scalar.
+ ///
+ /// Source .
+ /// Scalar value.
+ /// The result of the multiplication with a scalar as an output parameter.
+ public static void Multiply(Vector4 value1, float scaleFactor, out Vector4 result)
+ {
+ result.mW = value1.mW * scaleFactor;
+ result.mX = value1.mX * scaleFactor;
+ result.mY = value1.mY * scaleFactor;
+ result.mZ = value1.mZ * scaleFactor;
+ }
+
+ ///
+ /// Creates a new that contains a multiplication of two vectors.
+ ///
+ /// Source .
+ /// Source .
+ /// The result of the vector multiplication as an output parameter.
+ public static void Multiply(ref Vector4 value1, ref Vector4 value2, out Vector4 result)
+ {
+ result.mW = value1.mW * value2.mW;
+ result.mX = value1.mX * value2.mX;
+ result.mY = value1.mY * value2.mY;
+ result.mZ = value1.mZ * value2.mZ;
+ }
+
+ ///
+ /// Creates a new that contains the specified vector inversion.
+ ///
+ /// Source .
+ /// The result of the vector inversion.
+ public static Vector4 Negate(Vector4 value)
+ {
+ return Vector4(-value.mX, -value.mY, -value.mZ, -value.mW);
+ }
+
+ ///
+ /// Creates a new that contains the specified vector inversion.
+ ///
+ /// Source .
+ /// The result of the vector inversion as an output parameter.
+ public static void Negate(ref Vector4 value, out Vector4 result)
+ {
+ result.mX = -value.mX;
+ result.mY = -value.mY;
+ result.mZ = -value.mZ;
+ result.mW = -value.mW;
+ }
+
+ ///
+ /// Creates a new that contains a normalized values from another vector.
+ ///
+ /// Source .
+ /// Unit vector.
+ public static Vector4 Normalize(Vector4 vector)
+ {
+ float factor = 1.0f / (float) Math.Sqrt(
+ (vector.mX * vector.mX) +
+ (vector.mY * vector.mY) +
+ (vector.mZ * vector.mZ) +
+ (vector.mW * vector.mW)
+ );
+ return Vector4(
+ vector.mX * factor,
+ vector.mY * factor,
+ vector.mZ * factor,
+ vector.mW * factor
+ );
+ }
+
+ ///
+ /// Creates a new that contains a normalized values from another vector.
+ ///
+ /// Source .
+ /// Unit vector as an output parameter.
+ public static void Normalize(Vector4 vector, out Vector4 result)
+ {
+ float factor = 1.0f / (float) Math.Sqrt(
+ (vector.mX * vector.mX) +
+ (vector.mY * vector.mY) +
+ (vector.mZ * vector.mZ) +
+ (vector.mW * vector.mW)
+ );
+ result.mX = vector.mX * factor;
+ result.mY = vector.mY * factor;
+ result.mZ = vector.mZ * factor;
+ result.mW = vector.mW * factor;
+ }
+
+ ///
+ /// Creates a new that contains cubic interpolation of the specified vectors.
+ ///
+ /// Source .
+ /// Source .
+ /// Weighting value.
+ /// Cubic interpolation of the specified vectors.
+ public static Vector4 SmoothStep(Vector4 value1, Vector4 value2, float amount)
+ {
+ return Vector4(
+ Math.SmoothStep(value1.mX, value2.mX, amount),
+ Math.SmoothStep(value1.mY, value2.mY, amount),
+ Math.SmoothStep(value1.mZ, value2.mZ, amount),
+ Math.SmoothStep(value1.mW, value2.mW, amount)
+ );
+ }
+
+ ///
+ /// Creates a new that contains cubic interpolation of the specified vectors.
+ ///
+ /// Source .
+ /// Source .
+ /// Weighting value.
+ /// Cubic interpolation of the specified vectors as an output parameter.
+ public static void SmoothStep(
+ Vector4 value1,
+ Vector4 value2,
+ float amount,
+ out Vector4 result
+ ) {
+ result.mX = Math.SmoothStep(value1.mX, value2.mX, amount);
+ result.mY = Math.SmoothStep(value1.mY, value2.mY, amount);
+ result.mZ = Math.SmoothStep(value1.mZ, value2.mZ, amount);
+ result.mW = Math.SmoothStep(value1.mW, value2.mW, amount);
+ }
+
+ ///
+ /// Creates a new that contains subtraction of on from a another.
+ ///
+ /// Source .
+ /// Source .
+ /// The result of the vector subtraction.
+ public static Vector4 Subtract(Vector4 value1, Vector4 value2)
+ {
+ var value1;
+ value1.mW -= value2.mW;
+ value1.mX -= value2.mX;
+ value1.mY -= value2.mY;
+ value1.mZ -= value2.mZ;
+ return value1;
+ }
+
+ ///
+ /// Creates a new that contains subtraction of on from a another.
+ ///
+ /// Source .
+ /// Source .
+ /// The result of the vector subtraction as an output parameter.
+ public static void Subtract(Vector4 value1, Vector4 value2, out Vector4 result)
+ {
+ result.mW = value1.mW - value2.mW;
+ result.mX = value1.mX - value2.mX;
+ result.mY = value1.mY - value2.mY;
+ result.mZ = value1.mZ - value2.mZ;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 2d-vector by the specified .
+ ///
+ /// Source .
+ /// The transformation .
+ /// Transformed .
+ public static Vector4 Transform(Vector2 position, Matrix4 matrix)
+ {
+ Vector4 result;
+ Transform(position, matrix, out result);
+ return result;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 3d-vector by the specified .
+ ///
+ /// Source .
+ /// The transformation .
+ /// Transformed .
+ public static Vector4 Transform(Vector3 position, Matrix4 matrix)
+ {
+ Vector4 result;
+ Transform(position, matrix, out result);
+ return result;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 4d-vector by the specified .
+ ///
+ /// Source .
+ /// The transformation .
+ /// Transformed .
+ public static Vector4 Transform(Vector4 vector, Matrix4 matrix)
+ {
+ return Transform(vector, matrix);
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 2d-vector by the specified .
+ ///
+ /// Source .
+ /// The transformation .
+ /// Transformed as an output parameter.
+ public static void Transform(Vector2 position, Matrix4 matrix, out Vector4 result)
+ {
+ result = Vector4(
+ (position.mX * matrix.m00) + (position.mY * matrix.m01) + matrix.m03,
+ (position.mX * matrix.m10) + (position.mY * matrix.m11) + matrix.m13,
+ (position.mX * matrix.m20) + (position.mY * matrix.m21) + matrix.m23,
+ (position.mX * matrix.m30) + (position.mY * matrix.m31) + matrix.m33
+ );
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 3d-vector by the specified .
+ ///
+ /// Source .
+ /// The transformation .
+ /// Transformed as an output parameter.
+ public static void Transform(Vector3 position, Matrix4 matrix, out Vector4 result)
+ {
+ float x = (
+ (position.mX * matrix.m00) +
+ (position.mY * matrix.m01) +
+ (position.mZ * matrix.m02) +
+ matrix.m03
+ );
+ float y = (
+ (position.mX * matrix.m10) +
+ (position.mY * matrix.m11) +
+ (position.mZ * matrix.m12) +
+ matrix.m13
+ );
+ float z = (
+ (position.mX * matrix.m20) +
+ (position.mY * matrix.m21) +
+ (position.mZ * matrix.m22) +
+ matrix.m23
+ );
+ float w = (
+ (position.mX * matrix.m30) +
+ (position.mY * matrix.m31) +
+ (position.mZ * matrix.m32) +
+ matrix.m33
+ );
+ result.mX = x;
+ result.mY = y;
+ result.mZ = z;
+ result.mW = w;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 4d-vector by the specified .
+ ///
+ /// Source .
+ /// The transformation .
+ /// Transformed as an output parameter.
+ public static void Transform(Vector4 vector, Matrix4 matrix, out Vector4 result)
+ {
+ float x = (
+ (vector.mX * matrix.m00) +
+ (vector.mY * matrix.m01) +
+ (vector.mZ * matrix.m02) +
+ (vector.mW * matrix.m03)
+ );
+ float y = (
+ (vector.mX * matrix.m10) +
+ (vector.mY * matrix.m11) +
+ (vector.mZ * matrix.m12) +
+ (vector.mW * matrix.m13)
+ );
+ float z = (
+ (vector.mX * matrix.m20) +
+ (vector.mY * matrix.m21) +
+ (vector.mZ * matrix.m22) +
+ (vector.mW * matrix.m23)
+ );
+ float w = (
+ (vector.mX * matrix.m30) +
+ (vector.mY * matrix.m31) +
+ (vector.mZ * matrix.m32) +
+ (vector.mW * matrix.m33)
+ );
+ result.mX = x;
+ result.mY = y;
+ result.mZ = z;
+ result.mW = w;
+ }
+
+ ///
+ /// Apply transformation on all vectors within array of by the specified and places the results in an another array.
+ ///
+ /// Source array.
+ /// The transformation .
+ /// Destination array.
+ public static void Transform(
+ Vector4[] sourceArray,
+ Matrix4 matrix,
+ Vector4[] destinationArray
+ ) {
+ if (sourceArray == null)
+ {
+ Runtime.FatalError("sourceArray");
+ }
+ if (destinationArray == null)
+ {
+ Runtime.FatalError("destinationArray");
+ }
+ if (destinationArray.Count < sourceArray.Count)
+ {
+ Runtime.FatalError(
+ "destinationArray is too small to contain the result."
+ );
+ }
+ for (int i = 0; i < sourceArray.Count; i += 1)
+ {
+ Transform(
+ sourceArray[i],
+ matrix,
+ out destinationArray[i]
+ );
+ }
+ }
+
+ ///
+ /// Apply transformation on vectors within array of by the specified and places the results in an another array.
+ ///
+ /// Source array.
+ /// The starting index of transformation in the source array.
+ /// The transformation .
+ /// Destination array.
+ /// The starting index in the destination array, where the first should be written.
+ /// The number of vectors to be transformed.
+ public static void Transform(
+ Vector4[] sourceArray,
+ int sourceIndex,
+ Matrix4 matrix,
+ Vector4[] destinationArray,
+ int destinationIndex,
+ int length
+ ) {
+ if (sourceArray == null)
+ {
+ Runtime.FatalError("sourceArray");
+ }
+ if (destinationArray == null)
+ {
+ Runtime.FatalError("destinationArray");
+ }
+ if (destinationIndex + length > destinationArray.Count)
+ {
+ Runtime.FatalError(
+ "destinationArray is too small to contain the result."
+ );
+ }
+ if (sourceIndex + length > sourceArray.Count)
+ {
+ Runtime.FatalError(
+ "The combination of sourceIndex and length was greater than sourceArray.Length."
+ );
+ }
+ for (int i = 0; i < length; i += 1)
+ {
+ Transform(
+ sourceArray[i + sourceIndex],
+ matrix,
+ out destinationArray[i + destinationIndex]
+ );
+ }
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 2d-vector by the specified .
+ ///
+ /// Source .
+ /// The which contains rotation transformation.
+ /// Transformed .
+ public static Vector4 Transform(Vector2 value, Quaternion rotation)
+ {
+ Vector4 result;
+ Transform(value, rotation, out result);
+ return result;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 3d-vector by the specified .
+ ///
+ /// Source .
+ /// The which contains rotation transformation.
+ /// Transformed .
+ public static Vector4 Transform(Vector3 value, Quaternion rotation)
+ {
+ Vector4 result;
+ Transform(value, rotation, out result);
+ return result;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 4d-vector by the specified .
+ ///
+ /// Source .
+ /// The which contains rotation transformation.
+ /// Transformed .
+ public static Vector4 Transform(Vector4 value, Quaternion rotation)
+ {
+ Vector4 result;
+ Transform(value, rotation, out result);
+ return result;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 2d-vector by the specified .
+ ///
+ /// Source .
+ /// The which contains rotation transformation.
+ /// Transformed as an output parameter.
+ public static void Transform(
+ Vector2 value,
+ Quaternion rotation,
+ out Vector4 result
+ ) {
+ double xx = rotation.mX + rotation.mX;
+ double yy = rotation.mY + rotation.mY;
+ double zz = rotation.mZ + rotation.mZ;
+ double wxx = rotation.mW * xx;
+ double wyy = rotation.mW * yy;
+ double wzz = rotation.mW * zz;
+ double xxx = rotation.mX * xx;
+ double xyy = rotation.mX * yy;
+ double xzz = rotation.mX * zz;
+ double yyy = rotation.mY * yy;
+ double yzz = rotation.mY * zz;
+ double zzz = rotation.mZ * zz;
+ result.mX = (float) (
+ (double) value.mX * (1.0 - yyy - zzz) +
+ (double) value.mY * (xyy - wzz)
+ );
+ result.mY = (float) (
+ (double) value.mX * (xyy + wzz) +
+ (double) value.mY * (1.0 - xxx - zzz)
+ );
+ result.mZ = (float) (
+ (double) value.mX * (xzz - wyy) +
+ (double) value.mY * (yzz + wxx)
+ );
+ result.mW = 1.0f;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 3d-vector by the specified .
+ ///
+ /// Source .
+ /// The which contains rotation transformation.
+ /// Transformed as an output parameter.
+ public static void Transform(
+ Vector3 value,
+ Quaternion rotation,
+ out Vector4 result
+ ) {
+ double xx = rotation.mX + rotation.mX;
+ double yy = rotation.mY + rotation.mY;
+ double zz = rotation.mZ + rotation.mZ;
+ double wxx = rotation.mW * xx;
+ double wyy = rotation.mW * yy;
+ double wzz = rotation.mW * zz;
+ double xxx = rotation.mX * xx;
+ double xyy = rotation.mX * yy;
+ double xzz = rotation.mX * zz;
+ double yyy = rotation.mY * yy;
+ double yzz = rotation.mY * zz;
+ double zzz = rotation.mZ * zz;
+ result.mX = (float) (
+ (double) value.mX * (1.0 - yyy - zzz) +
+ (double) value.mY * (xyy - wzz) +
+ (double) value.mZ * (xzz + wyy)
+ );
+ result.mY = (float) (
+ (double) value.mX * (xyy + wzz) +
+ (double) value.mY * (1.0 - xxx - zzz) +
+ (double) value.mZ * (yzz - wxx)
+ );
+ result.mZ = (float) (
+ (double) value.mX * (xzz - wyy) +
+ (double) value.mY * (yzz + wxx) +
+ (double) value.mZ * (1.0 - xxx - yyy)
+ );
+ result.mW = 1.0f;
+ }
+
+ ///
+ /// Creates a new that contains a transformation of 4d-vector by the specified .
+ ///
+ /// Source .
+ /// The which contains rotation transformation.
+ /// Transformed as an output parameter.
+ public static void Transform(
+ Vector4 value,
+ Quaternion rotation,
+ out Vector4 result
+ ) {
+ double xx = rotation.mX + rotation.mX;
+ double yy = rotation.mY + rotation.mY;
+ double zz = rotation.mZ + rotation.mZ;
+ double wxx = rotation.mW * xx;
+ double wyy = rotation.mW * yy;
+ double wzz = rotation.mW * zz;
+ double xxx = rotation.mX * xx;
+ double xyy = rotation.mX * yy;
+ double xzz = rotation.mX * zz;
+ double yyy = rotation.mY * yy;
+ double yzz = rotation.mY * zz;
+ double zzz = rotation.mZ * zz;
+ result.mX = (float) (
+ (double) value.mX * (1.0 - yyy - zzz) +
+ (double) value.mY * (xyy - wzz) +
+ (double) value.mZ * (xzz + wyy)
+ );
+ result.mY = (float) (
+ (double) value.mX * (xyy + wzz) +
+ (double) value.mY * (1.0 - xxx - zzz) +
+ (double) value.mZ * (yzz - wxx)
+ );
+ result.mZ = (float) (
+ (double) value.mX * (xzz - wyy) +
+ (double) value.mY * (yzz + wxx) +
+ (double) value.mZ * (1.0 - xxx - yyy)
+ );
+ result.mW = value.mW;
+ }
+
+ ///
+ /// Apply transformation on all vectors within array of by the specified and places the results in an another array.
+ ///
+ /// Source array.
+ /// The which contains rotation transformation.
+ /// Destination array.
+ public static void Transform(
+ Vector4[] sourceArray,
+ Quaternion rotation,
+ Vector4[] destinationArray
+ ) {
+ if (sourceArray == null)
+ {
+ Runtime.FatalError("sourceArray");
+ }
+ if (destinationArray == null)
+ {
+ Runtime.FatalError("destinationArray");
+ }
+ if (destinationArray.Count < sourceArray.Count)
+ {
+ Runtime.FatalError(
+ "destinationArray is too small to contain the result."
+ );
+ }
+ for (int i = 0; i < sourceArray.Count; i += 1)
+ {
+ Transform(
+ sourceArray[i],
+ rotation,
+ out destinationArray[i]
+ );
+ }
+ }
+
+ ///
+ /// Apply transformation on vectors within array of by the specified and places the results in an another array.
+ ///
+ /// Source array.
+ /// The starting index of transformation in the source array.
+ /// The which contains rotation transformation.
+ /// Destination array.
+ /// The starting index in the destination array, where the first should be written.
+ /// The number of vectors to be transformed.
+ public static void Transform(
+ Vector4[] sourceArray,
+ int sourceIndex,
+ Quaternion rotation,
+ Vector4[] destinationArray,
+ int destinationIndex,
+ int length
+ ) {
+ if (sourceArray == null)
+ {
+ Runtime.FatalError("sourceArray");
+ }
+ if (destinationArray == null)
+ {
+ Runtime.FatalError("destinationArray");
+ }
+ if (destinationIndex + length > destinationArray.Count)
+ {
+ Runtime.FatalError(
+ "destinationArray is too small to contain the result."
+ );
+ }
+ if (sourceIndex + length > sourceArray.Count)
+ {
+ Runtime.FatalError(
+ "The combination of sourceIndex and length was greater than sourceArray.Length."
+ );
+ }
+ for (int i = 0; i < length; i += 1)
+ {
+ Transform(
+ sourceArray[i + sourceIndex],
+ rotation,
+ out destinationArray[i + destinationIndex]
+ );
+ }
+ }
+
+ #endregion
+
+ #region Public Static Operators
+
+ public static Vector4 operator -(Vector4 value)
+ {
+ return Vector4(-value.mX, -value.mY, -value.mZ, -value.mW);
+ }
+
+ public static bool operator ==(Vector4 value1, Vector4 value2)
+ {
+ return ( value1.mX == value2.mX &&
+ value1.mY == value2.mY &&
+ value1.mZ == value2.mZ &&
+ value1.mW == value2.mW );
+ }
+
+ public static bool operator !=(Vector4 value1, Vector4 value2)
+ {
+ return !(value1 == value2);
+ }
+
+ public static Vector4 operator +(Vector4 value1, Vector4 value2)
+ {
+ var value1;
+ value1.mW += value2.mW;
+ value1.mX += value2.mX;
+ value1.mY += value2.mY;
+ value1.mZ += value2.mZ;
+ return value1;
+ }
+
+ public static Vector4 operator -(Vector4 value1, Vector4 value2)
+ {
+ var value1;
+ value1.mW -= value2.mW;
+ value1.mX -= value2.mX;
+ value1.mY -= value2.mY;
+ value1.mZ -= value2.mZ;
+ return value1;
+ }
+
+ public static Vector4 operator *(Vector4 value1, Vector4 value2)
+ {
+ var value1;
+ value1.mW *= value2.mW;
+ value1.mX *= value2.mX;
+ value1.mY *= value2.mY;
+ value1.mZ *= value2.mZ;
+ return value1;
+ }
+
+ public static Vector4 operator *(Vector4 value1, float scaleFactor)
+ {
+ var value1;
+ value1.mW *= scaleFactor;
+ value1.mX *= scaleFactor;
+ value1.mY *= scaleFactor;
+ value1.mZ *= scaleFactor;
+ return value1;
+ }
+
+ public static Vector4 operator *(float scaleFactor, Vector4 value1)
+ {
+ var value1;
+ value1.mW *= scaleFactor;
+ value1.mX *= scaleFactor;
+ value1.mY *= scaleFactor;
+ value1.mZ *= scaleFactor;
+ return value1;
+ }
+
+ public static Vector4 operator /(Vector4 value1, Vector4 value2)
+ {
+ var value1;
+ value1.mW /= value2.mW;
+ value1.mX /= value2.mX;
+ value1.mY /= value2.mY;
+ value1.mZ /= value2.mZ;
+ return value1;
+ }
+
+ public static Vector4 operator /(Vector4 value1, float divider)
+ {
+ var value1;
+ float factor = 1f / divider;
+ value1.mW *= factor;
+ value1.mX *= factor;
+ value1.mY *= factor;
+ value1.mZ *= factor;
+ return value1;
+ }
+
+ #endregion
+ }
+}
diff --git a/BeefLibs/Beefy2D/src/geom/Viewport.bf b/BeefLibs/Beefy2D/src/geom/Viewport.bf
new file mode 100644
index 00000000..019f66b7
--- /dev/null
+++ b/BeefLibs/Beefy2D/src/geom/Viewport.bf
@@ -0,0 +1,59 @@
+// This file contains portions of code from the FNA project (github.com/FNA-XNA/FNA),
+// released under the Microsoft Public License
+
+using Beefy.geom;
+using System.Diagnostics;
+
+namespace Beefy.geom
+{
+ struct Viewport
+ {
+ private int mX;
+ private int mY;
+ private int mWidth;
+ private int mHeight;
+ private float mMinDepth;
+ private float mMaxDepth;
+
+ public this(int x, int y, int width, int height,float minDepth,float maxDepth)
+ {
+ this.mX = x;
+ this.mY = y;
+ this.mWidth = width;
+ this.mHeight = height;
+ this.mMinDepth = minDepth;
+ this.mMaxDepth = maxDepth;
+ }
+
+ /// Unprojects a Vector3 from screen space into world space.
+ /// @param source The Vector3 to unproject.
+ /// @param projection The projection
+ /// @param view The view
+ /// @param world The world
+ public Vector3 Unproject(Vector3 source, Matrix4 projection, Matrix4 view, Matrix4 world)
+ {
+ var source;
+
+ Matrix4 matrix = Matrix4.Invert(Matrix4.Multiply(Matrix4.Multiply(world, view), projection));
+ source.mX = (((source.mX - this.mX) / ((float) this.mWidth)) * 2f) - 1f;
+ source.mY = -((((source.mY - this.mY) / ((float) this.mHeight)) * 2f) - 1f);
+ source.mZ = (source.mZ - this.mMinDepth) / (this.mMaxDepth - this.mMinDepth);
+ Vector3 vector = Vector3.Transform(source, matrix);
+ float a = (((source.mX * matrix.m30) + (source.mY * matrix.m31)) + (source.mZ * matrix.m32)) + matrix.m33;
+ if (!WithinEpsilon(a, 1f))
+ {
+ vector.mX = vector.mX / a;
+ vector.mY = vector.mY / a;
+ vector.mZ = vector.mZ / a;
+ }
+ return vector;
+
+ }
+
+ private static bool WithinEpsilon(float a, float b)
+ {
+ float num = a - b;
+ return ((-1.401298E-45f <= num) && (num <= float.Epsilon));
+ }
+ }
+}
diff --git a/BeefLibs/Beefy2D/src/gfx/Graphics.bf b/BeefLibs/Beefy2D/src/gfx/Graphics.bf
index 1e38a957..1db845cc 100644
--- a/BeefLibs/Beefy2D/src/gfx/Graphics.bf
+++ b/BeefLibs/Beefy2D/src/gfx/Graphics.bf
@@ -442,7 +442,10 @@ namespace Beefy.gfx
//static unsafe extern void Gfx_DrawIndexedVertices2D(void* vtxData, int vtxCount, int* idxData, int idxCount, float a, float b, float c, float d, float tx, float ty, float z);
[CallingConvention(.Stdcall), CLink]
- static extern void Gfx_DrawIndexedVertices2D(int32 vertexSize, void* vtxData, int32 vtxCount, uint16* idxData, int32 idxCount, float a, float b, float c, float d, float tx, float ty, float z);
+ static extern void Gfx_DrawIndexedVertices(int32 vertexSize, void* vtxData, int32 vtxCount, uint16* idxData, int32 idxCount);
+
+ [CallingConvention(.Stdcall), CLink]
+ static extern void Gfx_DrawIndexedVertices2D(int32 vertexSize, void* vtxData, int32 vtxCount, uint16* idxData, int32 idxCount, float a, float b, float c, float d, float tx, float ty, float z);
[CallingConvention(.Stdcall), CLink]
static extern void Gfx_SetShaderConstantData(int32 usageIdx, int32 slotIdx, void* data, int32 size);
@@ -794,14 +797,28 @@ namespace Beefy.gfx
public void DrawIndexedVertices(VertexDefinition vertexDef, void* vertices, int vtxCount, uint16[] indices)
{
- Gfx_DrawIndexedVertices2D(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices.CArray(), (int32)indices.Count,
- mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth);
+ if (vertexDef.mPosition2DOffset != -1)
+ {
+ Gfx_DrawIndexedVertices2D(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices.CArray(), (int32)indices.Count,
+ mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth);
+ }
+ else
+ {
+ Gfx_DrawIndexedVertices(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices.CArray(), (int32)indices.Count);
+ }
}
public void DrawIndexedVertices(VertexDefinition vertexDef, void* vertices, int vtxCount, uint16* indices, int idxCount)
{
- Gfx_DrawIndexedVertices2D(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices, (int32)idxCount,
- mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth);
+ if (vertexDef.mPosition2DOffset != -1)
+ {
+ Gfx_DrawIndexedVertices2D(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices, (int32)idxCount,
+ mMatrix.a, mMatrix.b, mMatrix.c, mMatrix.d, mMatrix.tx, mMatrix.ty, ZDepth);
+ }
+ else
+ {
+ Gfx_DrawIndexedVertices(vertexDef.mVertexSize, vertices, (int32)vtxCount, indices, (int32)idxCount);
+ }
}
public void SetVertexShaderConstantData(int slotIdx, void* data, int size)
diff --git a/BeefLibs/Beefy2D/src/gfx/Model.bf b/BeefLibs/Beefy2D/src/gfx/Model.bf
index aeda2ed7..a587b106 100644
--- a/BeefLibs/Beefy2D/src/gfx/Model.bf
+++ b/BeefLibs/Beefy2D/src/gfx/Model.bf
@@ -48,7 +48,13 @@ namespace Beefy.gfx
#if !STUDIO_CLIENT
extension ModelDef
- {
+ {
+ public enum ModelCreateFlags
+ {
+ None = 0,
+ NoSetRenderState = 1
+ }
+
public class Animation
{
public void* mNativeModelDefAnimation;
@@ -103,11 +109,20 @@ namespace Beefy.gfx
extern static void* Res_OpenModel(char8* fileName, char8* baseDir, void* nativeVertexDef);
[CallingConvention(.Stdcall), CLink]
- extern static void* ModelDef_CreateModelInstance(void* nativeModel);
+ extern static void* ModelDef_CreateModelInstance(void* nativeModel, ModelCreateFlags flags);
+
+ [CallingConvention(.Stdcall), CLink]
+ extern static void ModelDef_Compact(void* nativeModel);
+
+ [CallingConvention(.Stdcall), CLink]
+ extern static void ModelDef_SetBaseDir(void* nativeModel, char8* baseDir);
[CallingConvention(.Stdcall), CLink]
extern static char8* ModelDef_GetInfo(void* nativeModel);
+ [CallingConvention(.Stdcall), CLink]
+ extern static void ModelDef_GetBounds(void* nativeModel, out Vector3 min, out Vector3 max);
+
[CallingConvention(.Stdcall), CLink]
extern static float ModelDef_GetFrameRate(void* nativeModel);
@@ -123,6 +138,9 @@ namespace Beefy.gfx
[CallingConvention(.Stdcall), CLink]
extern static void ModelDef_SetTextures(void* nativeModel, int32 meshIdx, int32 primitivesIdx, char8** paths, int32 pathCount);
+ [CallingConvention(.Stdcall), CLink]
+ extern static bool ModelDef_RayIntersect(void* nativeModel, Matrix4 worldMtx, Vector3 origin, Vector3 vec, out Vector3 outIntersect, out float outDistance);
+
[CallingConvention(.Stdcall), CLink]
extern static Span Res_SerializeModel(void* nativeModel);
@@ -147,20 +165,20 @@ namespace Beefy.gfx
public static ModelDef LoadModel(String fileName, String baseDir)
{
void* nativeModelDef = null;
- if (fileName.EndsWith(".bfm", .OrdinalIgnoreCase))
- nativeModelDef = Res_OpenModel(fileName, baseDir, VertexDef.sVertexDefinition.mNativeVertexDefinition);
+ if (fileName.EndsWith(".gltf", .OrdinalIgnoreCase))
+ nativeModelDef = Res_OpenGLTF(fileName, baseDir, VertexDef.sVertexDefinition.mNativeVertexDefinition);
else if (fileName.EndsWith(".fbx", .OrdinalIgnoreCase))
nativeModelDef = Res_OpenFBX(fileName, VertexDef.sVertexDefinition.mNativeVertexDefinition);
else
- nativeModelDef = Res_OpenGLTF(fileName, baseDir, VertexDef.sVertexDefinition.mNativeVertexDefinition);
+ nativeModelDef = Res_OpenModel(fileName, baseDir, VertexDef.sVertexDefinition.mNativeVertexDefinition);
if (nativeModelDef == null)
return null;
return new ModelDef(nativeModelDef);
}
- public ModelInstance CreateInstance()
+ public ModelInstance CreateInstance(ModelCreateFlags flags = .None)
{
- void* nativeModelInstance = ModelDef_CreateModelInstance(mNativeModelDef);
+ void* nativeModelInstance = ModelDef_CreateModelInstance(mNativeModelDef, flags);
if (nativeModelInstance == null)
return null;
var modelInstance = new ModelInstance(nativeModelInstance, this);
@@ -177,6 +195,21 @@ namespace Beefy.gfx
str.Append(ModelDef_GetInfo(mNativeModelDef));
}
+ public void GetBounds(out Vector3 min, out Vector3 max)
+ {
+ ModelDef_GetBounds(mNativeModelDef, out min, out max);
+ }
+
+ public void Compact()
+ {
+ ModelDef_Compact(mNativeModelDef);
+ }
+
+ public void SetBaseDir(StringView baseDir)
+ {
+ ModelDef_SetBaseDir(mNativeModelDef, baseDir.ToScopeCStr!());
+ }
+
public void SetTextures(int meshIdx, int primitivesIdx, Span paths)
{
ModelDef_SetTextures(mNativeModelDef, (.)meshIdx, (.)primitivesIdx, paths.Ptr, (.)paths.Length);
@@ -187,6 +220,11 @@ namespace Beefy.gfx
var span = Res_SerializeModel(mNativeModelDef);
data.AddRange(span);
}
+
+ public bool RayIntersect(Matrix4 worldMtx, Vector3 origin, Vector3 vec, out Vector3 outIntersect, out float outDistance)
+ {
+ return ModelDef_RayIntersect(mNativeModelDef, worldMtx, origin, vec, out outIntersect, out outDistance);
+ }
}
public class ModelInstance : RenderCmd
diff --git a/BeefLibs/Beefy2D/src/gfx/RenderState.bf b/BeefLibs/Beefy2D/src/gfx/RenderState.bf
index 24026320..4eb3b4fe 100644
--- a/BeefLibs/Beefy2D/src/gfx/RenderState.bf
+++ b/BeefLibs/Beefy2D/src/gfx/RenderState.bf
@@ -19,6 +19,12 @@ namespace Beefy.gfx
Always
}
+ public enum Topology
+ {
+ TriangleList,
+ LineList
+ }
+
#if !STUDIO_CLIENT
public class RenderState
{
@@ -34,6 +40,9 @@ namespace Beefy.gfx
[CallingConvention(.Stdcall), CLink]
static extern void RenderState_SetTexWrap(void* renderState, bool texWrap);
+ [CallingConvention(.Stdcall), CLink]
+ static extern void RenderState_SetWireframe(void* renderState, bool wireframe);
+
[CallingConvention(.Stdcall), CLink]
static extern void RenderState_DisableClip(void* renderState);
@@ -46,6 +55,9 @@ namespace Beefy.gfx
[CallingConvention(.Stdcall), CLink]
static extern void RenderState_SetDepthWrite(void* nativeRenderState, int32 depthWrite);
+ [CallingConvention(.Stdcall), CLink]
+ static extern void RenderState_SetTopology(void* nativeRenderState, int32 topology);
+
public void* mNativeRenderState;
public bool mIsFromDefaultRenderState;
@@ -115,6 +127,22 @@ namespace Beefy.gfx
RenderState_SetTexWrap(mNativeRenderState, value);
}
}
+
+ public bool Wireframe
+ {
+ set
+ {
+ RenderState_SetWireframe(mNativeRenderState, value);
+ }
+ }
+
+ public Topology Topology
+ {
+ set
+ {
+ RenderState_SetTopology(mNativeRenderState, (.)value);
+ }
+ }
}
#else
public class RenderState
diff --git a/BeefLibs/corlib/src/Math.bf b/BeefLibs/corlib/src/Math.bf
index 415c7564..ccd1aab0 100644
--- a/BeefLibs/corlib/src/Math.bf
+++ b/BeefLibs/corlib/src/Math.bf
@@ -24,7 +24,9 @@ namespace System
private static float[7] sRoundPower10Single = .(
1E0f, 1E1f, 1E2f, 1E3f, 1E4f, 1E5f, 1E6f);
-
+
+ private static float sMachineEpsilonFloat = GetMachineEpsilonFloat();
+
public const double PI_d = 3.14159265358979323846;
public const double E_d = 2.7182818284590452354;
public const float PI_f = 3.14159265358979323846f;
@@ -48,6 +50,33 @@ namespace System
public static extern float Floor(float f);
public static extern double Floor(double d);
+ public static bool WithinEpsilon(float a, float b)
+ {
+ return Math.Abs(a - b) < sMachineEpsilonFloat;
+ }
+
+ ///
+ /// Find the current machine's Epsilon for the float data type.
+ /// (That is, the largest float, e, where e == 0.0f is true.)
+ ///
+ private static float GetMachineEpsilonFloat()
+ {
+ float machineEpsilon = 1.0f;
+ float comparison;
+
+ /* Keep halving the working value of machineEpsilon until we get a number that
+ * when added to 1.0f will still evaluate as equal to 1.0f.
+ */
+ repeat
+ {
+ machineEpsilon *= 0.5f;
+ comparison = 1.0f + machineEpsilon;
+ }
+ while (comparison > 1.0f);
+
+ return machineEpsilon;
+ }
+
private static float InternalRound(float value, int32 digits, MidpointRounding mode)
{
if (Abs(value) < cSingleRoundLimit)
@@ -476,5 +505,125 @@ namespace System
{
return ((val) + (align - 1)) & ~(align - 1);
}
+
+ /// Interpolates between two values using a cubic equation.
+ /// @param name Source value.
+ /// @param name Source value.
+ /// @param name Weighting value.
+ /// @returns Interpolated value.
+ public static float SmoothStep(float value1, float value2, float amount)
+ {
+ /* It is expected that 0 < amount < 1.
+ * If amount < 0, return value1.
+ * If amount > 1, return value2.
+ */
+ float result = Clamp(amount, 0f, 1f);
+ result = Hermite(value1, 0f, value2, 0f, result);
+
+ return result;
+ }
+
+ /// Performs a Hermite spline interpolation.
+ /// @param value1 Source position.
+ /// @param tangent1 Source tangent.
+ /// @param value2 Source position.
+ /// @param tangent2 Source tangent.
+ /// @param amount Weighting factor.
+ /// @returns The result of the Hermite spline interpolation.
+ public static float Hermite(
+ float value1,
+ float tangent1,
+ float value2,
+ float tangent2,
+ float amount
+ ) {
+ /* All transformed to double not to lose precision
+ * Otherwise, for high numbers of param:amount the result is NaN instead
+ * of Infinity.
+ */
+ double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount;
+ double result;
+ double sCubed = s * s * s;
+ double sSquared = s * s;
+
+ if (WithinEpsilon(amount, 0f))
+ {
+ result = value1;
+ }
+ else if (WithinEpsilon(amount, 1f))
+ {
+ result = value2;
+ }
+ else
+ {
+ result = (
+ ((2 * v1 - 2 * v2 + t2 + t1) * sCubed) +
+ ((3 * v2 - 3 * v1 - 2 * t1 - t2) * sSquared) +
+ (t1 * s) +
+ v1
+ );
+ }
+
+ return (float) result;
+ }
+
+ /// Returns the Cartesian coordinate for one axis of a point that is defined by a
+ /// given triangle and two normalized barycentric (areal) coordinates.
+ ///
+ /// The coordinate on one axis of vertex 1 of the defining triangle.
+ ///
+ ///
+ /// The coordinate on the same axis of vertex 2 of the defining triangle.
+ ///
+ ///
+ /// The coordinate on the same axis of vertex 3 of the defining triangle.
+ ///
+ ///
+ /// The normalized barycentric (areal) coordinate b2, equal to the weighting factor
+ /// for vertex 2, the coordinate of which is specified in value2.
+ ///
+ /// @param amount2
+ /// The normalized barycentric (areal) coordinate b3, equal to the weighting factor
+ /// for vertex 3, the coordinate of which is specified in value3.
+ ///
+ /// @returns Cartesian coordinate of the specified point with respect to the axis being used.
+ public static float Barycentric(
+ float value1,
+ float value2,
+ float value3,
+ float amount1,
+ float amount2
+ ) {
+ return value1 + (value2 - value1) * amount1 + (value3 - value1) * amount2;
+ }
+
+ /// Performs a Catmull-Rom interpolation using the specified positions.
+ /// @param value1 The first position in the interpolation.
+ /// @param value2">The second position in the interpolation.
+ /// @param value3">The third position in the interpolation.
+ /// @param value4">The fourth position in the interpolation.
+ /// @param name="amount">Weighting factor.
+ /// @returns A position that is the result of the Catmull-Rom interpolation.
+ public static float CatmullRom(
+ float value1,
+ float value2,
+ float value3,
+ float value4,
+ float amount
+ ) {
+ /* Using formula from http://www.mvps.org/directx/articles/catmull/
+ * Internally using doubles not to lose precision.
+ */
+ double amountSquared = amount * amount;
+ double amountCubed = amountSquared * amount;
+ return (float) (
+ 0.5 *
+ (
+ ((2.0 * value2 + (value3 - value1) * amount) +
+ ((2.0 * value1 - 5.0 * value2 + 4.0 * value3 - value4) * amountSquared) +
+ (3.0 * value2 - value1 - 3.0 * value3 + value4) * amountCubed)
+ )
+ );
+ }
}
}
diff --git a/BeefySysLib/BeefySysLib.cpp b/BeefySysLib/BeefySysLib.cpp
index 00e9cfa1..d75ef692 100644
--- a/BeefySysLib/BeefySysLib.cpp
+++ b/BeefySysLib/BeefySysLib.cpp
@@ -610,6 +610,23 @@ BF_EXPORT void BF_CALLTYPE Gfx_DrawQuads(TextureSegment* textureSegment, Default
}
}
+BF_EXPORT void BF_CALLTYPE Gfx_DrawIndexedVertices(int vertexSize, void* vtxData, int vtxCount, uint16* idxData, int idxCount)
+{
+ DrawLayer* drawLayer = gBFApp->mRenderDevice->mCurDrawLayer;
+
+ uint16 idxOfs;
+ void* drawBatchVtxPtr;
+ uint16* drawBatchIdxPtr;
+ gBFApp->mRenderDevice->mCurDrawLayer->AllocIndexed(vtxCount, idxCount, (void**)&drawBatchVtxPtr, &drawBatchIdxPtr, &idxOfs);
+ BF_ASSERT(gBFApp->mRenderDevice->mCurDrawLayer->mCurDrawBatch->mVtxSize == vertexSize);
+
+ uint16* idxPtr = idxData;
+ for (int idxIdx = 0; idxIdx < idxCount; idxIdx++)
+ *(drawBatchIdxPtr++) = *(idxPtr++) + idxOfs;
+
+ memcpy(drawBatchVtxPtr, vtxData, vertexSize * vtxCount);
+}
+
BF_EXPORT void BF_CALLTYPE Gfx_DrawIndexedVertices2D(int vertexSize, void* vtxData, int vtxCount, uint16* idxData, int idxCount, float a, float b, float c, float d, float tx, float ty, float z)
{
DrawLayer* drawLayer = gBFApp->mRenderDevice->mCurDrawLayer;
@@ -683,6 +700,11 @@ BF_EXPORT void BF_CALLTYPE RenderState_SetTexWrap(RenderState* renderState, bool
renderState->SetTexWrap(texWrap);
}
+BF_EXPORT void BF_CALLTYPE RenderState_SetWireframe(RenderState* renderState, bool wireframe)
+{
+ renderState->SetWireframe(wireframe);
+}
+
BF_EXPORT void BF_CALLTYPE RenderState_SetClip(RenderState* renderState, float x, float y, float width, float height)
{
BF_ASSERT((width >= 0) && (height >= 0));
@@ -720,6 +742,11 @@ BF_EXPORT void BF_CALLTYPE RenderState_SetDepthWrite(RenderState* renderState, i
renderState->SetWriteDepthBuffer(depthWrite != 0);
}
+BF_EXPORT void BF_CALLTYPE RenderState_SetTopology(RenderState* renderState, int topology)
+{
+ renderState->SetTopology((Topology3D)topology);
+}
+
BF_EXPORT Shader* BF_CALLTYPE Gfx_LoadShader(const char* fileName, VertexDefinition* vertexDefinition)
{
return gBFApp->mRenderDevice->LoadShader(fileName, vertexDefinition);
diff --git a/BeefySysLib/BeefySysLib.vcxproj b/BeefySysLib/BeefySysLib.vcxproj
index 0dc3da96..dc76d66c 100644
--- a/BeefySysLib/BeefySysLib.vcxproj
+++ b/BeefySysLib/BeefySysLib.vcxproj
@@ -1936,11 +1936,13 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"
+
+
@@ -2166,6 +2168,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"
+
@@ -2176,6 +2179,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"
+
diff --git a/BeefySysLib/BeefySysLib.vcxproj.filters b/BeefySysLib/BeefySysLib.vcxproj.filters
index 39b9bb6f..a4e09430 100644
--- a/BeefySysLib/BeefySysLib.vcxproj.filters
+++ b/BeefySysLib/BeefySysLib.vcxproj.filters
@@ -713,6 +713,12 @@
src\util
+
+ src\util
+
+
+ src\util
+
@@ -1087,6 +1093,12 @@
src\util
+
+ src\util
+
+
+ src\util
+
diff --git a/BeefySysLib/gfx/ModelDef.cpp b/BeefySysLib/gfx/ModelDef.cpp
index 78f7f553..95fbaa86 100644
--- a/BeefySysLib/gfx/ModelDef.cpp
+++ b/BeefySysLib/gfx/ModelDef.cpp
@@ -6,6 +6,10 @@
#include "util/TLSingleton.h"
#include "FileStream.h"
#include "MemStream.h"
+#include "util/MathUtils.h"
+#include "util/Sphere.h"
+#include "util/HashSet.h"
+#include "util/BeefPerf.h"
#pragma warning(disable:4190)
@@ -50,14 +54,30 @@ void Beefy::ModelAnimation::GetJointTranslation(int jointIdx, float frameNum, Mo
//
-BF_EXPORT ModelInstance* BF_CALLTYPE ModelDef_CreateModelInstance(ModelDef* modelDef)
+BF_EXPORT ModelInstance* BF_CALLTYPE ModelDef_CreateModelInstance(ModelDef* modelDef, ModelCreateFlags flags)
{
- return gBFApp->mRenderDevice->CreateModelInstance(modelDef);
+ return gBFApp->mRenderDevice->CreateModelInstance(modelDef, flags);
+}
+
+BF_EXPORT void ModelDef_Compact(ModelDef* modelDef)
+{
+ modelDef->Compact();
+}
+
+BF_EXPORT void ModelDef_GetBounds(ModelDef* modelDef, Vector3& min, Vector3& max)
+{
+ modelDef->GetBounds(min, max);
+}
+
+BF_EXPORT void ModelDef_SetBaseDir(ModelDef* modelDef, char* baseDIr)
+{
+ modelDef->mLoadDir = baseDIr;
}
BF_EXPORT const char* BF_CALLTYPE ModelDef_GetInfo(ModelDef* modelDef)
{
String& outString = *gModelDef_TLStrReturn.Get();
+ outString.Clear();
for (int meshIdx = 0; meshIdx < (int)modelDef->mMeshes.mSize; meshIdx++)
{
auto mesh = modelDef->mMeshes[meshIdx];
@@ -105,6 +125,11 @@ BF_EXPORT void BF_CALLTYPE ModelDef_SetTextures(ModelDef* modelDef, int32 meshId
prims.mTexPaths.Add(paths[i]);
}
+BF_EXPORT bool BF_CALLTYPE ModelDef_RayIntersect(ModelDef* modelDef, const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance)
+{
+ return modelDef->RayIntersect(worldMtx, origin, vec, outIntersect, outDistance);
+}
+
BF_EXPORT void BF_CALLTYPE ModelDefAnimation_GetJointTranslation(ModelAnimation* modelAnimation, int jointIdx, float frame, ModelJointTranslation* outJointTranslation)
{
modelAnimation->GetJointTranslation(jointIdx, frame, outJointTranslation);
@@ -126,6 +151,11 @@ BF_EXPORT void BF_CALLTYPE ModelDefAnimation_Clip(ModelAnimation* modelAnimation
modelAnimation->mFrames.RemoveRange(numFrames, modelAnimation->mFrames.Count() - numFrames);
}
+ModelDef::ModelDef()
+{
+ mFlags = Flags_None;
+}
+
ModelDef::~ModelDef()
{
for (auto& materialInstance : mMaterials)
@@ -134,6 +164,459 @@ ModelDef::~ModelDef()
}
}
+void ModelDef::Compact()
+{
+ for (auto& mesh : mMeshes)
+ {
+ for (auto& prims : mesh.mPrimitives)
+ {
+ Array vtxMap;
+ vtxMap.Insert(0, -1, prims.mVertices.mSize);
+ int newVtxIdx = 0;
+
+ for (uint16 vtxIdx : prims.mIndices)
+ {
+ if (vtxMap[vtxIdx] == -1)
+ vtxMap[vtxIdx] = newVtxIdx++;
+ }
+
+ if (newVtxIdx >= prims.mVertices.mSize)
+ continue;
+
+ for (uint16& vtxIdx : prims.mIndices)
+ vtxIdx = vtxMap[vtxIdx];
+
+ Array oldVertices = prims.mVertices;
+ prims.mVertices.Resize(newVtxIdx);
+ for (int oldVtxIdx = 0; oldVtxIdx < oldVertices.mSize; oldVtxIdx++)
+ {
+ int newVtxIdx = vtxMap[oldVtxIdx];
+ if (newVtxIdx != -1)
+ prims.mVertices[newVtxIdx] = oldVertices[oldVtxIdx];
+ }
+ }
+ }
+}
+
+void ModelDef::CalcBounds()
+{
+ int vtxCount = 0;
+
+ for (auto& mesh : mMeshes)
+ {
+ for (auto& prims : mesh.mPrimitives)
+ {
+ for (auto& vtx : prims.mVertices)
+ {
+ if (vtxCount == 0)
+ {
+ mBounds.mMin = vtx.mPosition;
+ mBounds.mMax = vtx.mPosition;
+ }
+ else
+ {
+ mBounds.mMin.mX = BF_MIN(mBounds.mMin.mX, vtx.mPosition.mX);
+ mBounds.mMin.mY = BF_MIN(mBounds.mMin.mY, vtx.mPosition.mY);
+ mBounds.mMin.mZ = BF_MIN(mBounds.mMin.mZ, vtx.mPosition.mZ);
+
+ mBounds.mMax.mX = BF_MAX(mBounds.mMax.mX, vtx.mPosition.mX);
+ mBounds.mMax.mY = BF_MAX(mBounds.mMax.mY, vtx.mPosition.mY);
+ mBounds.mMax.mZ = BF_MAX(mBounds.mMax.mZ, vtx.mPosition.mZ);
+ }
+
+ vtxCount++;
+ }
+ }
+ }
+
+ mFlags = (Flags)(mFlags | Flags_HasBounds);
+}
+
+void ModelDef::GetBounds(Vector3& min, Vector3& max)
+{
+ if ((mFlags & Flags_HasBounds) == 0)
+ CalcBounds();
+
+ min = mBounds.mMin;
+ max = mBounds.mMax;
+}
+
+#define SWAP(x, y) { auto temp = x; x = y; y = temp; }
+#define N (sizeof(A)/sizeof(A[0]))
+
+// Partition using Lomuto partition scheme
+static int partition(float a[], int left, int right, int pIndex)
+{
+ // pick `pIndex` as a pivot from the array
+ float pivot = a[pIndex];
+
+ // Move pivot to end
+ SWAP(a[pIndex], a[right]);
+
+ // elements less than the pivot will be pushed to the left of `pIndex`;
+ // elements more than the pivot will be pushed to the right of `pIndex`;
+ // equal elements can go either way
+ pIndex = left;
+
+ // each time we find an element less than or equal to the pivot, `pIndex`
+ // is incremented, and that element would be placed before the pivot.
+ for (int i = left; i < right; i++)
+ {
+ if (a[i] <= pivot)
+ {
+ SWAP(a[i], a[pIndex]);
+ pIndex++;
+ }
+ }
+
+ // move pivot to its final place
+ SWAP(a[pIndex], a[right]);
+
+ // return `pIndex` (index of the pivot element)
+ return pIndex;
+}
+
+// Returns the k'th smallest element in the list within `left…right`
+// (i.e., `left <= k <= right`). The search space within the array is
+// changing for each round – but the list is still the same size.
+// Thus, `k` does not need to be updated with each round.
+static float quickselect(float A[], int left, int right, int k)
+{
+ // If the array contains only one element, return that element
+ if (left == right) {
+ return A[left];
+ }
+
+ // select `pIndex` between left and right
+ int pIndex = left + rand() % (right - left + 1);
+
+ pIndex = partition(A, left, right, pIndex);
+
+ // The pivot is in its final sorted position
+ if (k == pIndex) {
+ return A[k];
+ }
+
+ // if `k` is less than the pivot index
+ else if (k < pIndex) {
+ return quickselect(A, left, pIndex - 1, k);
+ }
+
+ // if `k` is more than the pivot index
+ else {
+ return quickselect(A, pIndex + 1, right, k);
+ }
+}
+
+void ModelDef::GenerateCollisionData()
+{
+ BP_ZONE("ModelDef::GenerateCollisionData");
+
+ mFlags = (Flags)(mFlags | Flags_HasBVH);
+
+ int statsWorkItrs = 0;
+ int statsWorkTris = 0;
+
+ BF_ASSERT(mBVNodes.IsEmpty());
+
+ struct _WorkEntry
+ {
+ int mParentNodeIdx;
+ int mTriWorkIdx;
+ int mTriWorkCount;
+ };
+
+ Array triWorkList;
+
+ int triCount = 0;
+ for (auto& mesh : mMeshes)
+ {
+ for (auto& prims : mesh.mPrimitives)
+ {
+ int startIdx = mBVIndices.mSize;
+ triCount += prims.mIndices.mSize / 3;
+ triWorkList.Reserve(triWorkList.mSize + triCount);
+ mBVIndices.Reserve(mBVIndices.mSize + prims.mIndices.mSize);
+ mBVVertices.Reserve(mBVVertices.mSize + prims.mVertices.mSize);
+
+ for (int triIdx = 0; triIdx < triCount; triIdx++)
+ triWorkList.Add(startIdx / 3 + triIdx);
+
+ for (auto idx : prims.mIndices)
+ {
+ mBVIndices.Add(idx + startIdx);
+ }
+
+ for (auto& vtx : prims.mVertices)
+ mBVVertices.Add(vtx.mPosition);
+ }
+ }
+
+ Array<_WorkEntry> workList;
+
+ _WorkEntry workEntry;
+ workEntry.mParentNodeIdx = -1;
+ workEntry.mTriWorkIdx = 0;
+ workEntry.mTriWorkCount = triWorkList.mSize;
+ workList.Add(workEntry);
+
+ Array points;
+ points.Reserve(triWorkList.mSize);
+ Array centers;
+ centers.Reserve(triWorkList.mSize);
+ Array left;
+ left.Reserve(triWorkList.mSize);
+ Array right;
+ right.Reserve(triWorkList.mSize);
+
+ mBVTris.Reserve(triWorkList.mSize * 2);
+
+ while (!workList.IsEmpty())
+ {
+ auto workEntry = workList.back();
+ workList.pop_back();
+
+ statsWorkItrs++;
+ statsWorkTris += workEntry.mTriWorkCount;
+
+ centers.Clear();
+ left.Clear();
+ right.Clear();
+
+ int nodeIdx = mBVNodes.mSize;
+ mBVNodes.Add(ModelBVNode());
+
+ if (workEntry.mParentNodeIdx != -1)
+ {
+ auto& bvParent = mBVNodes[workEntry.mParentNodeIdx];
+ if (bvParent.mLeft == -1)
+ bvParent.mLeft = nodeIdx;
+ else
+ {
+ BF_ASSERT(bvParent.mRight == -1);
+ bvParent.mRight = nodeIdx;
+ }
+ }
+
+ for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++)
+ {
+ bool inLeft = false;
+ bool inRight = false;
+
+ int triIdx = triWorkList.mVals[workEntry.mTriWorkIdx + triIdxIdx];
+ for (int triVtxIdx = 0; triVtxIdx < 3; triVtxIdx++)
+ {
+ int vtxIdx = mBVIndices.mVals[triIdx * 3 + triVtxIdx];
+ bool isNewVtx = false;
+
+ int32* valuePtr = NULL;
+ auto& vtx = mBVVertices[vtxIdx];
+
+ auto& bvNode = mBVNodes[nodeIdx];
+ if ((triIdxIdx == 0) && (triVtxIdx == 0))
+ {
+ bvNode.mBoundAABB.mMin = vtx;
+ bvNode.mBoundAABB.mMax = vtx;
+ }
+ else
+ {
+ bvNode.mBoundAABB.mMin.mX = BF_MIN(bvNode.mBoundAABB.mMin.mX, vtx.mX);
+ bvNode.mBoundAABB.mMin.mY = BF_MIN(bvNode.mBoundAABB.mMin.mY, vtx.mY);
+ bvNode.mBoundAABB.mMin.mZ = BF_MIN(bvNode.mBoundAABB.mMin.mZ, vtx.mZ);
+
+ bvNode.mBoundAABB.mMax.mX = BF_MAX(bvNode.mBoundAABB.mMax.mX, vtx.mX);
+ bvNode.mBoundAABB.mMax.mY = BF_MAX(bvNode.mBoundAABB.mMax.mY, vtx.mY);
+ bvNode.mBoundAABB.mMax.mZ = BF_MAX(bvNode.mBoundAABB.mMax.mZ, vtx.mZ);
+ }
+ }
+ }
+
+ //mBVNodes[nodeIdx].mBoundSphere = Sphere::MiniBall(points.mVals, points.mSize);
+
+ bool didSplit = false;
+
+ if (workEntry.mTriWorkCount > 4)
+ {
+ int splitPlane = 0;
+ float splitWidth = 0;
+
+ // Split along widest AABB dimension
+ for (int dimIdx = 0; dimIdx < 3; dimIdx++)
+ {
+ float minVal = 0;
+ float maxVal = 0;
+
+ for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++)
+ {
+ int triIdx = triWorkList.mVals[workEntry.mTriWorkIdx + triIdxIdx];
+ for (int triVtxIdx = 0; triVtxIdx < 3; triVtxIdx++)
+ {
+ int vtxIdx = mBVIndices.mVals[triIdx * 3 + triVtxIdx];
+ const Vector3& vtx = mBVVertices.mVals[vtxIdx];
+
+ float coord = ((float*)&vtx)[dimIdx];
+ if ((triIdxIdx == 0) && (triVtxIdx == 0))
+ {
+ minVal = coord;
+ maxVal = coord;
+ }
+ else
+ {
+ minVal = BF_MIN(minVal, coord);
+ maxVal = BF_MAX(maxVal, coord);
+ }
+ }
+ }
+
+ float width = maxVal - minVal;
+ if (width > splitWidth)
+ {
+ splitPlane = dimIdx;
+ splitWidth = width;
+ }
+ }
+
+ centers.SetSize(workEntry.mTriWorkCount);
+ for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++)
+ {
+ int triIdx = triWorkList.mVals[workEntry.mTriWorkIdx + triIdxIdx];
+ float coordAcc = 0;
+ for (int triVtxIdx = 0; triVtxIdx < 3; triVtxIdx++)
+ {
+ int vtxIdx = mBVIndices.mVals[triIdx * 3 + triVtxIdx];
+ const Vector3& vtx = mBVVertices.mVals[vtxIdx];
+ float coord = ((float*)&vtx)[splitPlane];
+ coordAcc += coord;
+ }
+
+ float coordAvg = coordAcc / 3;
+ centers.mVals[triIdxIdx] = coordAvg;
+ }
+
+ float centerCoord = quickselect(centers.mVals, 0, centers.mSize - 1, centers.mSize / 2);
+
+// centers.Sort([](float lhs, float rhs) { return lhs < rhs; });
+// centerCoord = centers[centers.mSize / 2];
+
+ for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++)
+ {
+ bool inLeft = false;
+ bool inRight = false;
+
+ int triIdx = triWorkList.mVals[workEntry.mTriWorkIdx + triIdxIdx];
+ for (int triVtxIdx = 0; triVtxIdx < 3; triVtxIdx++)
+ {
+ int vtxIdx = mBVIndices.mVals[triIdx * 3 + triVtxIdx];
+ const Vector3& vtx = mBVVertices.mVals[vtxIdx];
+ float coord = ((float*)&vtx)[splitPlane];
+
+ if (coord < centerCoord)
+ inLeft = true;
+ else
+ inRight = true;
+ }
+
+ if (inLeft)
+ left.Add(triIdx);
+ if (inRight)
+ right.Add(triIdx);
+ }
+
+ // Don't split if the split didn't significantly separate the triangles
+ bool doSplit =
+ (left.mSize <= workEntry.mTriWorkCount * 0.85f) &&
+ (right.mSize <= workEntry.mTriWorkCount * 0.85f);
+
+ if (doSplit)
+ {
+ mBVNodes[nodeIdx].mKind = ModelBVNode::Kind_Branch;
+ mBVNodes[nodeIdx].mLeft = -1;
+ mBVNodes[nodeIdx].mRight = -1;
+
+ _WorkEntry childWorkEntry;
+
+ childWorkEntry.mParentNodeIdx = nodeIdx;
+ childWorkEntry.mTriWorkIdx = triWorkList.mSize;
+ childWorkEntry.mTriWorkCount = right.mSize;
+ workList.Add(childWorkEntry);
+ triWorkList.Insert(triWorkList.mSize, right.mVals, right.mSize);
+
+ childWorkEntry.mParentNodeIdx = nodeIdx;
+ childWorkEntry.mTriWorkIdx = triWorkList.mSize;
+ childWorkEntry.mTriWorkCount = left.mSize;
+ workList.Add(childWorkEntry);
+ triWorkList.Insert(triWorkList.mSize, left.mVals, left.mSize);
+
+ continue;
+ }
+ }
+
+ // Did not split
+ int triStartIdx = mBVTris.mSize;
+ //mBVTris.Reserve(mBVTris.mSize + workEntry.mTriWorkCount);
+ for (int triIdxIdx = 0; triIdxIdx < workEntry.mTriWorkCount; triIdxIdx++)
+ mBVTris.Add(triWorkList[workEntry.mTriWorkIdx + triIdxIdx]);
+
+ auto& bvNode = mBVNodes[nodeIdx];
+ bvNode.mKind = ModelBVNode::Kind_Leaf;
+ bvNode.mTriStartIdx = triStartIdx;
+ bvNode.mTriCount = workEntry.mTriWorkCount;
+ }
+
+ NOP;
+}
+
+void ModelDef::RayIntersect(ModelBVNode* bvNode, const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance)
+{
+// if (!RayIntersectsCircle(origin, vec, bvNode->mBoundSphere, NULL, NULL, NULL))
+// return false;
+ if (!RayIntersectsAABB(origin, vec, bvNode->mBoundAABB, NULL, NULL, NULL))
+ return;
+
+ if (bvNode->mKind == ModelBVNode::Kind_Branch)
+ {
+ bool hadIntersect = false;
+ for (int branchIdx = 0; branchIdx < 2; branchIdx++)
+ RayIntersect(&mBVNodes[(branchIdx == 0) ? bvNode->mLeft : bvNode->mRight], worldMtx, origin, vec, outIntersect, outDistance);
+ return;
+ }
+
+ for (int triIdxIdx = 0; triIdxIdx < bvNode->mTriCount; triIdxIdx++)
+ {
+ int triIdx = mBVTris[bvNode->mTriStartIdx + triIdxIdx];
+
+ Vector3 curIntersect;
+ float curDistance;
+ if (RayIntersectsTriangle(origin, vec,
+ mBVVertices[mBVIndices[triIdx*3+0]], mBVVertices[mBVIndices[triIdx*3+1]], mBVVertices[mBVIndices[triIdx*3+2]],
+ &curIntersect, &curDistance))
+ {
+ if (curDistance < outDistance)
+ {
+ outIntersect = curIntersect;
+ outDistance = curDistance;
+ }
+ }
+ }
+}
+
+bool ModelDef::RayIntersect(const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance)
+{
+ if ((mFlags & Flags_HasBounds) == 0)
+ CalcBounds();
+
+ if (!RayIntersectsAABB(origin, vec, mBounds, NULL, NULL, NULL))
+ return false;
+
+ if (mBVNodes.IsEmpty())
+ GenerateCollisionData();
+
+ const float maxDist = 3.40282e+038f;
+ outDistance = maxDist;
+ RayIntersect(&mBVNodes[0], worldMtx, origin, vec, outIntersect, outDistance);
+ return outDistance != maxDist;
+}
+
ModelMaterialDef* ModelMaterialDef::CreateOrGet(const StringImpl& prefix, const StringImpl& path)
{
StringT<128> key = prefix;
@@ -211,7 +694,7 @@ public:
bool ReadFile(const StringImpl& fileName, const StringImpl& baseDir)
{
- if (fileName.Contains(':'))
+ if (fileName.StartsWith('@'))
{
int colon = (int)fileName.IndexOf(':');
String addrStr = fileName.Substring(1, colon - 1);
@@ -286,5 +769,7 @@ BF_EXPORT StringView BF_CALLTYPE Res_SerializeModel(ModelDef* modelDef)
}
String& outString = *gModelDef_TLStrReturn.Get();
+ outString.Clear();
+ outString.Append((char*)ms.GetPtr(), ms.GetSize());
return outString;
}
diff --git a/BeefySysLib/gfx/ModelDef.h b/BeefySysLib/gfx/ModelDef.h
index b4dd6362..8e040142 100644
--- a/BeefySysLib/gfx/ModelDef.h
+++ b/BeefySysLib/gfx/ModelDef.h
@@ -5,6 +5,8 @@
#include "util/Vector.h"
#include "util/Array.h"
#include "gfx/Texture.h"
+#include "util/Sphere.h"
+#include "util/MathUtils.h"
#include
NS_BF_BEGIN;
@@ -151,8 +153,6 @@ class ModelMesh
{
public:
String mName;
- //String mTexFileName;
- //String mBumpFileName;
Array mPrimitives;
};
@@ -166,19 +166,83 @@ public:
Array mChildren;
};
+class ModelBVNode
+{
+public:
+ enum Kind
+ {
+ Kind_None,
+ Kind_Branch,
+ Kind_Leaf
+ };
+
+public:
+ Sphere mBoundSphere;
+ AABB mBoundAABB;
+
+ union
+ {
+ struct
+ {
+ int mLeft;
+ int mRight;
+ };
+
+ struct
+ {
+ int mTriStartIdx;
+ int mTriCount;
+ };
+ };
+
+ Kind mKind;
+
+public:
+ ModelBVNode()
+ {
+ mKind = Kind_None;
+ }
+};
+
class ModelDef
{
+public:
+ enum Flags
+ {
+ Flags_None,
+ Flags_HasBounds,
+ Flags_HasBVH,
+ };
+
public:
String mLoadDir;
- float mFrameRate;
+ float mFrameRate;
Array mMeshes;
Array mJoints;
Array mAnims;
Array mNodes;
Array mMaterials;
+
+ Flags mFlags;
+ AABB mBounds;
+ Array mBVNodes;
+ Array mBVIndices;
+ Array mBVVertices;
+ Array mBVTris;
+protected:
+ void CalcBounds();
+ void RayIntersect(ModelBVNode* bvNode, const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance);
+
public:
+ ModelDef();
~ModelDef();
+
+ void Compact();
+ void GetBounds(Vector3& min, Vector3& max);
+
+ void GenerateCollisionData();
+ bool RayIntersect(const Matrix4& worldMtx, const Vector3& origin, const Vector3& vec, Vector3& outIntersect, float& outDistance);
};
NS_BF_END;
diff --git a/BeefySysLib/gfx/RenderDevice.cpp b/BeefySysLib/gfx/RenderDevice.cpp
index 50fc35db..61af4300 100644
--- a/BeefySysLib/gfx/RenderDevice.cpp
+++ b/BeefySysLib/gfx/RenderDevice.cpp
@@ -19,9 +19,11 @@ RenderState::RenderState()
mWriteDepthBuffer = false;
mCullMode = CullMode_None;
mDepthFunc = DepthFunc_Always;
+ mTopology = Topology3D_TriangleList;
mShader = NULL;
mClipped = false;
mTexWrap = false;
+ mWireframe = false;
}
RenderTarget::RenderTarget()
diff --git a/BeefySysLib/gfx/RenderDevice.h b/BeefySysLib/gfx/RenderDevice.h
index 6deb4973..1333aaef 100644
--- a/BeefySysLib/gfx/RenderDevice.h
+++ b/BeefySysLib/gfx/RenderDevice.h
@@ -143,6 +143,12 @@ enum CullMode : int8
CullMode_Back
};
+enum Topology3D : int8
+{
+ Topology3D_TriangleList,
+ Topology3D_LineLine
+};
+
enum TextureFlag : int8
{
TextureFlag_Additive = 1,
@@ -183,8 +189,10 @@ public:
DepthFunc mDepthFunc;
bool mClipped;
bool mTexWrap;
+ bool mWireframe;
Rect mClipRect;
CullMode mCullMode;
+ Topology3D mTopology;
public:
RenderState();
@@ -192,10 +200,12 @@ public:
virtual void SetShader(Shader* shader) { mShader = shader; }
virtual void SetTexWrap(bool wrap) { mTexWrap = wrap; }
+ virtual void SetWireframe(bool wireframe) { mWireframe = wireframe; }
virtual void SetClipped(bool clipped) { mClipped = clipped; }
virtual void SetClipRect(const Rect& rect) { mClipRect = rect; }
virtual void SetWriteDepthBuffer(bool writeDepthBuffer) { mWriteDepthBuffer = writeDepthBuffer; }
virtual void SetDepthFunc(DepthFunc depthFunc) { mDepthFunc = depthFunc; }
+ virtual void SetTopology(Topology3D topology) { mTopology = topology; }
};
class PoolData
@@ -241,6 +251,12 @@ public:
}
};
+enum ModelCreateFlags
+{
+ ModelCreateFlags_None = 0,
+ ModelCreateFlags_NoSetRenderState = 1
+};
+
class RenderDevice
{
public:
@@ -272,7 +288,7 @@ public:
virtual void RemoveRenderWindow(RenderWindow* renderWindow);
virtual RenderState* CreateRenderState(RenderState* srcRenderState);
- virtual ModelInstance* CreateModelInstance(ModelDef* modelDef) { return NULL; }
+ virtual ModelInstance* CreateModelInstance(ModelDef* modelDef, ModelCreateFlags flags) { return NULL; }
virtual VertexDefinition* CreateVertexDefinition(VertexDefData* elementData, int numElements);
virtual void FrameStart() = 0;
diff --git a/BeefySysLib/platform/win/DXRenderDevice.cpp b/BeefySysLib/platform/win/DXRenderDevice.cpp
index 34868e5a..12619328 100644
--- a/BeefySysLib/platform/win/DXRenderDevice.cpp
+++ b/BeefySysLib/platform/win/DXRenderDevice.cpp
@@ -143,9 +143,9 @@ static int GetBytesPerPixel(DXGI_FORMAT fmt, int& blockSize)
case DXGI_FORMAT_BC6H_TYPELESS: return 1;
case DXGI_FORMAT_BC6H_UF16: return 1;
case DXGI_FORMAT_BC6H_SF16: return 1;
- case DXGI_FORMAT_BC7_TYPELESS: return 1;
- case DXGI_FORMAT_BC7_UNORM: return 1;
- case DXGI_FORMAT_BC7_UNORM_SRGB: return 1;
+ case DXGI_FORMAT_BC7_TYPELESS: blockSize = 4; return 16;
+ case DXGI_FORMAT_BC7_UNORM: blockSize = 4; return 16;
+ case DXGI_FORMAT_BC7_UNORM_SRGB: blockSize = 4; return 16;
case DXGI_FORMAT_AYUV: return 1;
case DXGI_FORMAT_Y410: return 1;
case DXGI_FORMAT_Y416: return 1;
@@ -510,9 +510,17 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState)
DXRenderState* dxRenderState = (DXRenderState*)renderState;
DXShader* dxShader = (DXShader*)renderState->mShader;
- if ((renderState->mShader != mPhysRenderState->mShader) && (renderState->mShader != NULL))
+ if (renderState->mTopology != mPhysRenderState->mTopology)
{
- mD3DDeviceContext->PSSetSamplers(0, 1, mPhysRenderState->mTexWrap ? &mD3DWrapSamplerState : &mD3DDefaultSamplerState);
+ D3D_PRIMITIVE_TOPOLOGY topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ if (dxRenderState->mTopology == Topology3D_LineLine)
+ topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
+ mD3DDeviceContext->IASetPrimitiveTopology(topology);
+ }
+
+ if ((renderState->mShader != mPhysRenderState->mShader) && (renderState->mShader != NULL))
+ {
+ mD3DDeviceContext->PSSetSamplers(0, 1, renderState->mTexWrap ? &mD3DWrapSamplerState : &mD3DDefaultSamplerState);
mD3DDeviceContext->IASetInputLayout(dxShader->mD3DLayout);
mD3DDeviceContext->VSSetShader(dxShader->mD3DVertexShader, NULL, 0);
mD3DDeviceContext->PSSetShader(dxShader->mD3DPixelShader, NULL, 0);
@@ -591,6 +599,9 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState)
setRasterizerState = true;
}
+ if (renderState->mWireframe != mPhysRenderState->mWireframe)
+ setRasterizerState = true;
+
if (setRasterizerState)
{
if (dxRenderState->mD3DRasterizerState == NULL)
@@ -599,13 +610,13 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState)
{
D3D11_CULL_NONE,
D3D11_CULL_FRONT,
- D3D11_CULL_BACK
+ D3D11_CULL_BACK
};
D3D11_RASTERIZER_DESC rasterizerState;
rasterizerState.CullMode = cullModes[dxRenderState->mCullMode];
//rasterizerState.CullMode = D3D11_CULL_BACK;
- rasterizerState.FillMode = D3D11_FILL_SOLID;
+ rasterizerState.FillMode = renderState->mWireframe ? D3D11_FILL_WIREFRAME : D3D11_FILL_SOLID;
rasterizerState.FrontCounterClockwise = false;
rasterizerState.DepthBias = 0;
rasterizerState.DepthBiasClamp = 0;
@@ -701,7 +712,7 @@ struct DXModelVertex
Vector3 mTangent;
};
-ModelInstance* DXRenderDevice::CreateModelInstance(ModelDef* modelDef)
+ModelInstance* DXRenderDevice::CreateModelInstance(ModelDef* modelDef, ModelCreateFlags flags)
{
DXModelInstance* dxModelInstance = new DXModelInstance(modelDef);
@@ -718,16 +729,17 @@ ModelInstance* DXRenderDevice::CreateModelInstance(ModelDef* modelDef)
};
auto vertexDefinition = CreateVertexDefinition(vertexDefData, sizeof(vertexDefData) / sizeof(vertexDefData[0]));
- auto renderState = CreateRenderState(mDefaultRenderState);
- renderState->mShader = LoadShader(gBFApp->mInstallDir + "/shaders/ModelStd", vertexDefinition);
+ RenderState* renderState = NULL;
+ if ((flags & ModelCreateFlags_NoSetRenderState) == 0)
+ {
+ renderState = CreateRenderState(mDefaultRenderState);
+ renderState->mShader = LoadShader(gBFApp->mInstallDir + "/shaders/ModelStd", vertexDefinition);
+ renderState->mTexWrap = true;
+ renderState->mDepthFunc = DepthFunc_LessEqual;
+ renderState->mWriteDepthBuffer = true;
+ }
delete vertexDefinition;
-
- //renderState->mCullMode = CullMode_Front;
-
- renderState->mTexWrap = true;
- renderState->mDepthFunc = DepthFunc_LessEqual;
- renderState->mWriteDepthBuffer = true;
-
+
dxModelInstance->mRenderState = renderState;
////
@@ -1052,7 +1064,8 @@ DXModelInstance::~DXModelInstance()
void DXModelInstance::Render(RenderDevice* renderDevice, RenderWindow* renderWindow)
{
- SetRenderState();
+ if (mRenderState != NULL)
+ SetRenderState();
for (int meshIdx = 0; meshIdx < (int)mDXModelMeshs.size(); meshIdx++)
{
@@ -1064,28 +1077,7 @@ void DXModelInstance::Render(RenderDevice* renderDevice, RenderWindow* renderWin
for (auto primIdx = 0; primIdx < (int)dxMesh->mPrimitives.size(); primIdx++)
{
auto dxPrimitives = &dxMesh->mPrimitives[primIdx];
-
- if (dxPrimitives->mNumIndices == 11904)
- {
- NOP;
- }
-
- //TODO:
- if (dxPrimitives->mNumIndices == 48384)
- continue;
-
- if (::GetAsyncKeyState('1'))
- {
- if (dxPrimitives->mNumIndices != 9417)
- continue;
- }
- else if (::GetAsyncKeyState('2'))
- {
- if (dxPrimitives->mNumIndices != 3684)
- continue;
- }
-
if (dxPrimitives->mTextures.IsEmpty())
continue;
@@ -1103,14 +1095,12 @@ void DXModelInstance::Render(RenderDevice* renderDevice, RenderWindow* renderWin
}
void Beefy::DXModelInstance::CommandQueued(DrawLayer* drawLayer)
-{
-#ifndef BF_NO_FBX
+{
mRenderState = drawLayer->mRenderDevice->mCurRenderState;
-
BF_ASSERT(mRenderState->mShader->mVertexSize == sizeof(DXModelVertex));
-
drawLayer->mCurTextures[0] = NULL;
+#ifndef BF_NO_FBX
ModelAnimation* fbxAnim = &mModelDef->mAnims[0];
Matrix4 jointsMatrices[BF_MAX_NUM_BONES];
@@ -1560,8 +1550,7 @@ bool DXRenderDevice::Init(BFApp* app)
rasterizerState.AntialiasedLineEnable = false;
mD3DDevice->CreateRasterizerState(&rasterizerState, &dxRenderState->mD3DRasterizerState);
- mD3DDeviceContext->RSSetState(dxRenderState->mD3DRasterizerState);
-
+ mD3DDeviceContext->RSSetState(dxRenderState->mD3DRasterizerState);
mD3DDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D11BlendState* g_pBlendState = NULL;
diff --git a/BeefySysLib/platform/win/DXRenderDevice.h b/BeefySysLib/platform/win/DXRenderDevice.h
index f155d694..b9bce08d 100644
--- a/BeefySysLib/platform/win/DXRenderDevice.h
+++ b/BeefySysLib/platform/win/DXRenderDevice.h
@@ -95,7 +95,7 @@ typedef std::map DXShaderParamMap;
class DXShader : public Shader
{
-public:
+public:
ID3D11InputLayout* mD3DLayout;
ID3D11VertexShader* mD3DVertexShader;
ID3D11PixelShader* mD3DPixelShader;
@@ -302,7 +302,7 @@ public:
virtual void PhysSetRenderWindow(RenderWindow* renderWindow);
virtual void PhysSetRenderTarget(Texture* renderTarget) override;
virtual RenderState* CreateRenderState(RenderState* srcRenderState) override;
- virtual ModelInstance* CreateModelInstance(ModelDef* modelDef) override;
+ virtual ModelInstance* CreateModelInstance(ModelDef* modelDef, ModelCreateFlags flags) override;
public:
DXRenderDevice();
diff --git a/BeefySysLib/util/MathUtils.cpp b/BeefySysLib/util/MathUtils.cpp
new file mode 100644
index 00000000..c5399ebd
--- /dev/null
+++ b/BeefySysLib/util/MathUtils.cpp
@@ -0,0 +1,145 @@
+#include "MathUtils.h"
+#include "Sphere.h"
+
+USING_NS_BF;
+
+bool Beefy::RayIntersectsTriangle(const Vector3& rayOrigin, const Vector3& rayVector, const Vector3& vtx0, const Vector3& vtx1, const Vector3& vtx2, Vector3* outIntersectionPoint, float* outDistance)
+{
+ const float EPSILON = 0.0000001f;
+ Vector3 edge1, edge2, h, s, q;
+ float a, f, u, v;
+ edge1 = vtx1 - vtx0;
+ edge2 = vtx2 - vtx0;
+
+ h = Vector3::CrossProduct(rayVector, edge2);//rayVector.crossProduct(edge2);
+ a = Vector3::Dot(edge1, h); //edge1.dotProduct(h);
+ if (a > -EPSILON && a < EPSILON)
+ return false; // This ray is parallel to this triangle.
+ f = 1.0f / a;
+ s = rayOrigin - vtx0;
+ u = f * Vector3::Dot(s, h); //s.dotProduct(h);
+ if (u < 0.0 || u > 1.0)
+ return false;
+ q = Vector3::CrossProduct(s, edge1); //s.crossProduct(edge1);
+ v = f * Vector3::Dot(rayVector, q); //rayVector.dotProduct(q);
+ if (v < 0.0 || u + v > 1.0)
+ return false;
+ // At this stage we can compute t to find out where the intersection point is on the line.
+ float distance = f * Vector3::Dot(edge2, q); //edge2.dotProduct(q);
+ if (distance > EPSILON) // ray intersection
+ {
+ if (outIntersectionPoint != NULL)
+ *outIntersectionPoint = rayOrigin + rayVector * distance;
+ if (outDistance != NULL)
+ *outDistance = distance;
+ return true;
+ }
+ else // This means that there is a line intersection but not a ray intersection.
+ return false;
+}
+
+bool Beefy::RayIntersectsCircle(const Vector3& rayOrigin, const Vector3& rayVector, const Sphere& sphere, Vector3* outIntersectionPoint, Vector3* outNormal, float* outDistance)
+{
+ Vector3 e = sphere.mCenter - rayOrigin;
+ float rSq = sphere.mRadius * sphere.mRadius;
+
+ float eSq = e.GetMagnitudeSquare();
+ float a = Vector3::Dot(e, rayVector); // ray.direction is assumed to be normalized
+ float bSq = /*sqrtf(*/eSq - (a * a)/*)*/;
+ float f = sqrtf(fabsf((rSq)- /*(b * b)*/bSq));
+
+ // Assume normal intersection!
+ float t = a - f;
+
+ // No collision has happened
+ if (rSq - (eSq - a * a) < 0.0f)
+ {
+ return false;
+ }
+ // Ray starts inside the sphere
+ else if (eSq < rSq)
+ {
+ // Just reverse direction
+ t = a + f;
+ }
+
+ if (outDistance != NULL)
+ *outDistance = t;
+ if ((outIntersectionPoint != NULL) || (outNormal != NULL))
+ {
+ Vector3 intersect = rayOrigin + rayVector * t;
+ if (outIntersectionPoint != NULL)
+ *outIntersectionPoint = intersect;
+ if (outNormal != NULL)
+ *outNormal = Vector3::Normalize(intersect - sphere.mCenter);
+ }
+
+ return true;
+}
+
+bool Beefy::RayIntersectsAABB(const Vector3& rayOrigin, const Vector3& rayVector, const AABB& aabb, Vector3* outIntersectionPoint, Vector3* outNormal, float* outDistance)
+{
+ const Vector3& min = aabb.mMin;
+ const Vector3& max = aabb.mMax;
+
+ // Any component of direction could be 0!
+ // Address this by using a small number, close to
+ // 0 in case any of directions components are 0
+ float t1 = (min.mX - rayOrigin.mX) / ((fabs(rayVector.mX) < 0.00001f) ? 0.00001f : rayVector.mX);
+ float t2 = (max.mX - rayOrigin.mX) / ((fabs(rayVector.mX) < 0.00001f) ? 0.00001f : rayVector.mX);
+ float t3 = (min.mY - rayOrigin.mY) / ((fabs(rayVector.mY) < 0.00001f) ? 0.00001f : rayVector.mY);
+ float t4 = (max.mY - rayOrigin.mY) / ((fabs(rayVector.mY) < 0.00001f) ? 0.00001f : rayVector.mY);
+ float t5 = (min.mZ - rayOrigin.mZ) / ((fabs(rayVector.mZ) < 0.00001f) ? 0.00001f : rayVector.mZ);
+ float t6 = (max.mZ - rayOrigin.mZ) / ((fabs(rayVector.mZ) < 0.00001f) ? 0.00001f : rayVector.mZ);
+
+ float tmin = fmaxf(fmaxf(fminf(t1, t2), fminf(t3, t4)), fminf(t5, t6));
+ float tmax = fminf(fminf(fmaxf(t1, t2), fmaxf(t3, t4)), fmaxf(t5, t6));
+
+ // if tmax < 0, ray is intersecting AABB
+ // but entire AABB is being it's origin
+ if (tmax < 0) {
+ return false;
+ }
+
+ // if tmin > tmax, ray doesn't intersect AABB
+ if (tmin > tmax) {
+ return false;
+ }
+
+ float t_result = tmin;
+
+ // If tmin is < 0, tmax is closer
+ if (tmin < 0.0f) {
+ t_result = tmax;
+ }
+
+ if ((outIntersectionPoint != NULL) || (outDistance != NULL))
+ {
+ Vector3 hitPoint = rayOrigin + rayVector * t_result;
+ if (outIntersectionPoint != NULL)
+ *outIntersectionPoint = hitPoint;
+ if (outDistance != NULL)
+ *outDistance = (rayOrigin - hitPoint).GetMagnitude();
+ }
+
+ if (outNormal != NULL)
+ {
+ static Vector3 normals[] = {
+ Vector3(-1, 0, 0),
+ Vector3(1, 0, 0),
+ Vector3(0, -1, 0),
+ Vector3(0, 1, 0),
+ Vector3(0, 0, -1),
+ Vector3(0, 0, 1)
+ };
+ float t[] = { t1, t2, t3, t4, t5, t6 };
+
+ for (int i = 0; i < 6; ++i)
+ {
+ if (t_result == t[i])
+ *outNormal = normals[i];
+ }
+ }
+
+ return true;
+}
diff --git a/BeefySysLib/util/MathUtils.h b/BeefySysLib/util/MathUtils.h
new file mode 100644
index 00000000..fa3911be
--- /dev/null
+++ b/BeefySysLib/util/MathUtils.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "Vector.h"
+#include "Matrix4.h"
+
+NS_BF_BEGIN
+
+class AABB
+{
+public:
+ Vector3 mMin;
+ Vector3 mMax;
+};
+
+class Sphere;
+
+bool RayIntersectsTriangle(const Vector3& rayOrigin, const Vector3& rayVector, const Vector3& vtx0, const Vector3& vtx1, const Vector3& vtx2, Vector3* outIntersectionPoint, float* distance);
+bool RayIntersectsCircle(const Vector3& rayOrigin, const Vector3& rayVector, const Sphere& sphere, Vector3* outIntersectionPoint, Vector3* outNormal, float* outDistance);
+bool RayIntersectsAABB(const Vector3& rayOrigin, const Vector3& rayVector, const AABB& aabb, Vector3* outIntersectionPoint, Vector3* outNormal, float* outDistance);
+
+NS_BF_END
\ No newline at end of file
diff --git a/BeefySysLib/util/Matrix4.h b/BeefySysLib/util/Matrix4.h
index 5d9096d3..ce9844f2 100644
--- a/BeefySysLib/util/Matrix4.h
+++ b/BeefySysLib/util/Matrix4.h
@@ -109,6 +109,15 @@ public:
}
static Matrix4 CreateTransform(const Vector3& position, const Vector3& scale, const Quaternion& orientation);
+
+ static float Determinant(float m11, float m12, float m13,
+ float m21, float m22, float m23,
+ float m31, float m32, float m33)
+ {
+ return m11 * (m22 * m33 - m32 * m23) -
+ m21 * (m12 * m33 - m32 * m13) +
+ m31 * (m12 * m23 - m22 * m13);
+ }
};
NS_BF_END;
diff --git a/BeefySysLib/util/Sphere.cpp b/BeefySysLib/util/Sphere.cpp
new file mode 100644
index 00000000..fc939717
--- /dev/null
+++ b/BeefySysLib/util/Sphere.cpp
@@ -0,0 +1,195 @@
+#include "Sphere.h"
+#include "Matrix4.h"
+
+USING_NS_BF;
+
+static const float radiusEpsilon = 1e-4f; // NOTE: To avoid numerical inaccuracies
+
+Sphere::Sphere()
+{
+ mRadius = -1;
+}
+
+Sphere::Sphere(const Sphere& S)
+{
+ mRadius = S.mRadius;
+ mCenter = S.mCenter;
+}
+
+Sphere::Sphere(const Vector3& O)
+{
+ mRadius = 0 + radiusEpsilon;
+ mCenter = O;
+}
+
+Sphere::Sphere(const Vector3& O, float R)
+{
+ mRadius = R;
+ mCenter = O;
+}
+
+Sphere::Sphere(const Vector3& O, const Vector3& A)
+{
+ Vector3 a = A - O;
+
+ Vector3 o = a * 0.5f;
+
+ mRadius = o.GetMagnitude() + radiusEpsilon;
+ mCenter = O + o;
+}
+
+Sphere::Sphere(const Vector3& O, const Vector3& A, const Vector3& B)
+{
+ Vector3 a = A - O;
+ Vector3 b = B - O;
+
+ Vector3 axb = Vector3::CrossProduct(a, b);
+
+ float Denominator = 2.0f * Vector3::Dot(axb, axb);
+
+ Vector3 o =
+ ((Vector3::CrossProduct(axb, a) * Vector3::Dot(b, b)) +
+ (Vector3::CrossProduct(b, axb) * Vector3::Dot(a, a))) / Denominator;
+
+ mRadius = o.GetMagnitude() + radiusEpsilon;
+ mCenter = O + o;
+}
+
+Sphere::Sphere(const Vector3& O, const Vector3& A, const Vector3& B, const Vector3& C)
+{
+ Vector3 a = A - O;
+ Vector3 b = B - O;
+ Vector3 c = C - O;
+
+ float Denominator = 2.0f * Matrix4::Determinant(
+ a.mX, a.mY, a.mZ,
+ b.mX, b.mY, b.mZ,
+ c.mX, c.mY, c.mZ);
+
+ Vector3 o = (
+ (Vector3::CrossProduct(a, b) * Vector3::Dot(c, c)) +
+ (Vector3::CrossProduct(c, a) * Vector3::Dot(b, b)) +
+ (Vector3::CrossProduct(b, c) * Vector3::Dot(a, a))) / Denominator;
+
+ mRadius = o.GetMagnitude() + radiusEpsilon;
+ mCenter = O + o;
+}
+
+Sphere& Sphere::operator=(const Sphere& S)
+{
+ mRadius = S.mRadius;
+ mCenter = S.mCenter;
+
+ return *this;
+}
+
+float Sphere::GetDistance(const Vector3& P) const
+{
+ return Vector3::GetDistance(P, mCenter) - mRadius;
+}
+
+float Sphere::GetDistanceSquare(const Vector3& P) const
+{
+ return Vector3::GetDistanceSquare(P, mCenter) - mRadius * mRadius;
+}
+
+float Sphere::GetDistance(const Sphere& S, const Vector3& P)
+{
+ return Vector3::GetDistance(P, S.mCenter) - S.mRadius;
+}
+
+float Sphere::GetDistance(const Vector3& P, const Sphere& S)
+{
+ return Vector3::GetDistance(P, S.mCenter) - S.mRadius;
+}
+
+float Sphere::GetDistanceSquare(const Sphere& S, const Vector3& P)
+{
+ return Vector3::GetDistanceSquare(P, S.mCenter) - S.mRadius * S.mRadius;
+}
+
+float Sphere::GetDistanceSquare(const Vector3& P, const Sphere& S)
+{
+ return Vector3::GetDistanceSquare(P, S.mCenter) - S.mRadius * S.mRadius;
+}
+
+Sphere Sphere::RecurseMini(Vector3* P[], int p, int b)
+{
+ Sphere MB;
+
+ switch (b)
+ {
+ case 0:
+ MB = Sphere();
+ break;
+ case 1:
+ MB = Sphere(*P[-1]);
+ break;
+ case 2:
+ MB = Sphere(*P[-1], *P[-2]);
+ break;
+ case 3:
+ MB = Sphere(*P[-1], *P[-2], *P[-3]);
+ break;
+ case 4:
+ MB = Sphere(*P[-1], *P[-2], *P[-3], *P[-4]);
+ return MB;
+ }
+
+ for (int i = 0; i < p; i++)
+ {
+ if (MB.GetDistanceSquare(*P[i]) > 0) // Signed square distance to sphere
+ {
+ for (int j = i; j > 0; j--)
+ {
+ Vector3* T = P[j];
+ P[j] = P[j - 1];
+ P[j - 1] = T;
+ }
+
+ MB = RecurseMini(P + 1, i, b + 1);
+ }
+ }
+
+ return MB;
+}
+
+Sphere Sphere::MiniBall(Vector3 P[], int p)
+{
+ Vector3** L = new Vector3* [p];
+
+ for (int i = 0; i < p; i++)
+ L[i] = &P[i];
+
+ Sphere MB = RecurseMini(L, p);
+
+ delete[] L;
+
+ return MB;
+}
+
+Sphere Sphere::SmallBall(Vector3 P[], int p)
+{
+ Vector3 mCenter;
+ float mRadius = -1;
+
+ if (p > 0)
+ {
+ for (int i = 0; i < p; i++)
+ mCenter += P[i];
+
+ mCenter /= (float)p;
+
+ for (int i = 0; i < p; i++)
+ {
+ float d2 = Vector3::GetDistanceSquare(P[i], mCenter);
+
+ if (d2 > mRadius)
+ mRadius = d2;
+ }
+
+ mRadius = sqrtf(mRadius) + radiusEpsilon;
+ }
+
+ return Sphere(mCenter, mRadius);
+}
diff --git a/BeefySysLib/util/Sphere.h b/BeefySysLib/util/Sphere.h
new file mode 100644
index 00000000..95c3e3e0
--- /dev/null
+++ b/BeefySysLib/util/Sphere.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "Common.h"
+#include "Vector.h"
+
+NS_BF_BEGIN
+
+class Vector3;
+class Matrix4;
+
+class Sphere
+{
+public:
+ Vector3 mCenter;
+ float mRadius;
+
+ Sphere();
+ Sphere(const Sphere& X);
+ Sphere(const Vector3& O); // Point-Sphere
+ Sphere(const Vector3& O, float R); // Center and radius (not squared)
+ Sphere(const Vector3& O, const Vector3& A); // Sphere through two points
+ Sphere(const Vector3& O, const Vector3& A, const Vector3& B); // Sphere through three points
+ Sphere(const Vector3& O, const Vector3& A, const Vector3& B, const Vector3& C); // Sphere through four points
+
+ Sphere& operator=(const Sphere& S);
+
+ float GetDistance(const Vector3& P) const; // Distance from p to boundary of the Sphere
+ float GetDistanceSquare(const Vector3& P) const; // Square distance from p to boundary of the Sphere
+
+ static float GetDistance(const Sphere& S, const Vector3& P); // Distance from p to boundary of the Sphere
+ static float GetDistance(const Vector3& P, const Sphere& S); // Distance from p to boundary of the Sphere
+
+ static float GetDistanceSquare(const Sphere& S, const Vector3& P); // Square distance from p to boundary of the Sphere
+ static float GetDistanceSquare(const Vector3& P, const Sphere& S); // Square distance from p to boundary of the Sphere
+
+ static Sphere MiniBall(Vector3 P[], int p); // Smallest enclosing sphere
+ static Sphere SmallBall(Vector3 P[], int p); // Enclosing sphere approximation
+
+private:
+ static Sphere RecurseMini(Vector3* P[], int p, int b = 0);
+};
+
+NS_BF_END
diff --git a/BeefySysLib/util/Vector.cpp b/BeefySysLib/util/Vector.cpp
index fd8ae07f..5987ee35 100644
--- a/BeefySysLib/util/Vector.cpp
+++ b/BeefySysLib/util/Vector.cpp
@@ -4,6 +4,13 @@
USING_NS_BF;
+Vector3::Vector3()
+{
+ mX = 0;
+ mY = 0;
+ mZ = 0;
+}
+
Vector3::Vector3(float x, float y, float z)
{
mX = x;
@@ -16,6 +23,11 @@ float Vector3::GetMagnitude() const
return sqrtf(mX*mX + mY*mY + mZ*mZ);
}
+float Vector3::GetMagnitudeSquare() const
+{
+ return mX * mX + mY * mY + mZ * mZ;
+}
+
Vector3 Vector3::Normalize(const Vector3& vec)
{
float mag = vec.GetMagnitude();
@@ -39,6 +51,14 @@ Vector3 Vector3::CrossProduct(const Vector3& vec1, const Vector3& vec2)
}
Vector3 Vector3::Transform(const Vector3& vec, const Matrix4& matrix)
+{
+ return Vector3(
+ (matrix.m00 * vec.mX + matrix.m01 * vec.mY + matrix.m02 * vec.mZ + matrix.m03),
+ (matrix.m10 * vec.mX + matrix.m11 * vec.mY + matrix.m12 * vec.mZ + matrix.m13),
+ (matrix.m20 * vec.mX + matrix.m21 * vec.mY + matrix.m22 * vec.mZ + matrix.m23));
+}
+
+Vector3 Vector3::TransformW(const Vector3& vec, const Matrix4& matrix)
{
float fInvW = 1.0f / (matrix.m30 * vec.mX + matrix.m31 * vec.mY + matrix.m32 * vec.mZ + matrix.m33);
diff --git a/BeefySysLib/util/Vector.h b/BeefySysLib/util/Vector.h
index 1c21d285..200cac2c 100644
--- a/BeefySysLib/util/Vector.h
+++ b/BeefySysLib/util/Vector.h
@@ -36,13 +36,25 @@ public:
float mZ;
public:
- Vector3(float x = 0, float y = 0, float z = 0);
+ Vector3();
+ Vector3(float x, float y, float z);
float GetMagnitude() const;
+ float GetMagnitudeSquare() const;
static Vector3 Normalize(const Vector3& vec);
static float Dot(const Vector3& vec1, const Vector3& vec2);
static Vector3 CrossProduct(const Vector3& vec1, const Vector3& vec2);
+ static float GetDistance(const Vector3& v0, const Vector3& v1)
+ {
+ return (v0 - v1).GetMagnitude();
+ }
+
+ static float GetDistanceSquare(const Vector3& v0, const Vector3& v1)
+ {
+ return (v0 - v1).GetMagnitudeSquare();
+ }
+
bool operator==(const Vector3& check) const
{
return (mX == check.mX) && (mY == check.mY) && (mZ == check.mZ);
@@ -54,8 +66,9 @@ public:
}
static Vector3 Transform(const Vector3& vec, const Matrix4& matrix);
+ static Vector3 TransformW(const Vector3& vec, const Matrix4& matrix);
static Vector3 Transform(const Vector3& vec, const Quaternion& quat);
- static Vector3 Transform2(const Vector3& vec, const Quaternion& quat);
+ static Vector3 Transform2(const Vector3& vec, const Quaternion& quat);
static Vector3 Scale(const Vector3& vec, float scale)
{
@@ -77,6 +90,24 @@ public:
return Vector3(mX * scale, mY * scale, mZ * scale);
}
+ Vector3 operator /(float scale) const
+ {
+ return Vector3(mX / scale, mY / scale, mZ / scale);
+ }
+
+ float operator^(const Vector3& v) // Angle between vectors
+ {
+ return acosf(Dot(*this / this->GetMagnitude(), v / v.GetMagnitude()));
+ }
+
+ inline Vector3& operator += (const Vector3& vec)
+ {
+ mX += vec.mX;
+ mY += vec.mY;
+ mZ += vec.mZ;
+ return *this;
+ }
+
inline Vector3& operator -= (const Vector3& vec)
{
mX -= vec.mX;
@@ -85,6 +116,40 @@ public:
return *this;
}
+ inline Vector3& operator *= (float num)
+ {
+ mX *= num;
+ mY *= num;
+ mZ *= num;
+ return *this;
+ }
+
+ inline Vector3& operator /= (float num)
+ {
+ mX /= num;
+ mY /= num;
+ mZ /= num;
+ return *this;
+ }
+
+ inline Vector3 operator - (const Vector3& vec) const
+ {
+ Vector3 result;
+ result.mX = mX - vec.mX;
+ result.mY = mY - vec.mY;
+ result.mZ = mZ - vec.mZ;
+ return result;
+ }
+
+ inline Vector3 operator + (const Vector3& vec)
+ {
+ Vector3 result;
+ result.mX = mX + vec.mX;
+ result.mY = mY + vec.mY;
+ result.mZ = mZ + vec.mZ;
+ return result;
+ }
+
inline Vector3& operator *= (const Vector3& vec)
{
mX *= vec.mX;