From 39c140f44a9b896e50252a415eb05d9eb49503f8 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Tue, 25 May 2021 10:57:22 -0400 Subject: [PATCH] Additional 3d support --- BeefLibs/Beefy2D/src/geom/BoundingBox.bf | 14 + BeefLibs/Beefy2D/src/geom/Matrix4.bf | 772 +++++++++- BeefLibs/Beefy2D/src/geom/Plane.bf | 291 ++++ BeefLibs/Beefy2D/src/geom/Quaternion.bf | 84 +- BeefLibs/Beefy2D/src/geom/Vector3.bf | 67 +- BeefLibs/Beefy2D/src/geom/Vector4.bf | 1435 +++++++++++++++++++ BeefLibs/Beefy2D/src/geom/Viewport.bf | 59 + BeefLibs/Beefy2D/src/gfx/Graphics.bf | 27 +- BeefLibs/Beefy2D/src/gfx/Model.bf | 52 +- BeefLibs/Beefy2D/src/gfx/RenderState.bf | 28 + BeefLibs/corlib/src/Math.bf | 151 +- BeefySysLib/BeefySysLib.cpp | 27 + BeefySysLib/BeefySysLib.vcxproj | 4 + BeefySysLib/BeefySysLib.vcxproj.filters | 12 + BeefySysLib/gfx/ModelDef.cpp | 491 ++++++- BeefySysLib/gfx/ModelDef.h | 70 +- BeefySysLib/gfx/RenderDevice.cpp | 2 + BeefySysLib/gfx/RenderDevice.h | 18 +- BeefySysLib/platform/win/DXRenderDevice.cpp | 79 +- BeefySysLib/platform/win/DXRenderDevice.h | 4 +- BeefySysLib/util/MathUtils.cpp | 145 ++ BeefySysLib/util/MathUtils.h | 21 + BeefySysLib/util/Matrix4.h | 9 + BeefySysLib/util/Sphere.cpp | 195 +++ BeefySysLib/util/Sphere.h | 43 + BeefySysLib/util/Vector.cpp | 20 + BeefySysLib/util/Vector.h | 69 +- 27 files changed, 4063 insertions(+), 126 deletions(-) create mode 100644 BeefLibs/Beefy2D/src/geom/BoundingBox.bf create mode 100644 BeefLibs/Beefy2D/src/geom/Plane.bf create mode 100644 BeefLibs/Beefy2D/src/geom/Vector4.bf create mode 100644 BeefLibs/Beefy2D/src/geom/Viewport.bf create mode 100644 BeefySysLib/util/MathUtils.cpp create mode 100644 BeefySysLib/util/MathUtils.h create mode 100644 BeefySysLib/util/Sphere.cpp create mode 100644 BeefySysLib/util/Sphere.h 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;