1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00

Additional 3d support

This commit is contained in:
Brian Fiete 2021-05-25 10:57:22 -04:00
parent 369bb0640c
commit 39c140f44a
27 changed files with 4063 additions and 126 deletions

View file

@ -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;
}
}

View file

@ -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;
}
/// <summary>
/// Decomposes this matrix to translation, rotation and scale elements. Returns <c>true</c> if matrix can be decomposed; <c>false</c> otherwise.
/// </summary>
/// <param name="scale">Scale vector as an output parameter.
/// <param name="rotation">Rotation quaternion as an output parameter.
/// <param name="translation">Translation vector as an output parameter.
/// <returns><c>true</c> if matrix can be decomposed; <c>false</c> otherwise.</returns>
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;
}
/// <summary>
/// Returns a determinant of this matrix.
/// </summary>
/// <returns>Determinant of this matrix</returns>
/// <remarks>See more about determinant here - http://en.wikipedia.org/wiki/Determinant.
/// </remarks>
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)))
);
}
/// <summary>
/// Creates a new matrix for spherical billboarding that rotates around specified object position.
/// </summary>
/// <param name="objectPosition">Position of billboard object. It will rotate around that vector.
/// <param name="cameraPosition">The camera position.
/// <param name="cameraUpVector">The camera up vector.
/// <param name="cameraForwardVector">Optional camera forward vector.
/// <returns>The matrix for spherical billboarding.</returns>
public static Matrix4 CreateBillboard(
Vector3 objectPosition,
Vector3 cameraPosition,
Vector3 cameraUpVector,
Nullable<Vector3> cameraForwardVector
) {
Matrix4 result;
// Delegate to the other overload of the function to do the work
CreateBillboard(
objectPosition,
cameraPosition,
cameraUpVector,
cameraForwardVector,
out result
);
return result;
}
/// <summary>
/// Creates a new matrix for spherical billboarding that rotates around specified object position.
/// </summary>
/// <param name="objectPosition">Position of billboard object. It will rotate around that vector.
/// <param name="cameraPosition">The camera position.
/// <param name="cameraUpVector">The camera up vector.
/// <param name="cameraForwardVector">Optional camera forward vector.
/// <param name="result">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;
}
/// <summary>
/// Creates a new matrix for cylindrical billboarding that rotates around specified axis.
/// </summary>
/// <param name="objectPosition">Object position the billboard will rotate around.
/// <param name="cameraPosition">Camera position.
/// <param name="rotateAxis">Axis of billboard for rotation.
/// <param name="cameraForwardVector">Optional camera forward vector.
/// <param name="objectForwardVector">Optional object forward vector.
/// <returns>The matrix for cylindrical billboarding.</returns>
public static Matrix4 CreateConstrainedBillboard(
Vector3 objectPosition,
Vector3 cameraPosition,
Vector3 rotateAxis,
Nullable<Vector3> cameraForwardVector,
Nullable<Vector3> objectForwardVector
) {
Matrix4 result;
CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
cameraForwardVector,
objectForwardVector,
out result
);
return result;
}
/// <summary>
/// Creates a new matrix for cylindrical billboarding that rotates around specified axis.
/// </summary>
/// <param name="objectPosition">Object position the billboard will rotate around.
/// <param name="cameraPosition">Camera position.
/// <param name="rotateAxis">Axis of billboard for rotation.
/// <param name="cameraForwardVector">Optional camera forward vector.
/// <param name="objectForwardVector">Optional object forward vector.
/// <param name="result">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;
}
/// <summary>
/// Creates a new matrix which contains the rotation moment around specified axis.
/// </summary>
/// <param name="axis">The axis of rotation.
/// <param name="angle">The angle of rotation in radians.
/// <returns>The rotation matrix.</returns>
public static Matrix4 CreateFromAxisAngle(Vector3 axis, float angle)
{
Matrix4 result;
CreateFromAxisAngle(axis, angle, out result);
return result;
}
/// <summary>
/// Creates a new matrix which contains the rotation moment around specified axis.
/// </summary>
/// <param name="axis">The axis of rotation.
/// <param name="angle">The angle of rotation in radians.
/// <param name="result">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;
}
/// <summary>
/// Creates a new rotation matrix from a <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion"><see cref="Quaternion"/> of rotation moment.
/// <returns>The rotation matrix.</returns>
public static Matrix4 CreateFromQuaternion(Quaternion quaternion)
{
Matrix4 result;
CreateFromQuaternion(quaternion, out result);
return result;
}
/// <summary>
/// Creates a new rotation matrix from a <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion"><see cref="Quaternion"/> of rotation moment.
/// <param name="result">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.</returns>
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.</returns>
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 <see cref="Plane"/> 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 <see cref="Plane"/> 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");
}
}
}
}

View file

@ -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
{
/// <summary>
/// Defines the intersection between a <see cref="Plane"/> and a bounding volume.
/// </summary>
public enum PlaneIntersectionType
{
/// <summary>
/// There is no intersection, the bounding volume is in the negative half space of the plane.
/// </summary>
Front,
/// <summary>
/// There is no intersection, the bounding volume is in the positive half space of the plane.
/// </summary>
Back,
/// <summary>
/// The plane is intersected.
/// </summary>
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;
}
/// <summary>
/// Transforms a normalized plane by a matrix.
/// </summary>
/// <param name="plane">The normalized plane to transform.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>The transformed plane.</returns>
public static Plane Transform(Plane plane, Matrix4 matrix)
{
Plane result;
Transform(plane, matrix, out result);
return result;
}
/// <summary>
/// Transforms a normalized plane by a matrix.
/// </summary>
/// <param name="plane">The normalized plane to transform.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <param name="result">The transformed plane.</param>
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);
}
/// <summary>
/// Transforms a normalized plane by a quaternion rotation.
/// </summary>
/// <param name="plane">The normalized plane to transform.</param>
/// <param name="rotation">The quaternion rotation.</param>
/// <returns>The transformed plane.</returns>
public static Plane Transform(Plane plane, Quaternion rotation)
{
Plane result;
Transform(plane, rotation, out result);
return result;
}
/// <summary>
/// Transforms a normalized plane by a quaternion rotation.
/// </summary>
/// <param name="plane">The normalized plane to transform.</param>
/// <param name="rotation">The quaternion rotation.</param>
/// <param name="result">The transformed plane.</param>
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
}
}

View file

@ -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)

View file

@ -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
}
}*/
/// <summary>
/// Returns a <see>Vector3</see> pointing in the opposite
/// direction of <paramref name="value"/>.
/// </summary>
/// <param name="value">The vector to negate.</param>
/// <returns>The vector negation of <paramref name="value"/>.</returns>
public static Vector3 Negate(Vector3 value)
{
return .(-value.mX, -value.mY, -value.mZ);
}
/// <summary>
/// Stores a <see>Vector3</see> pointing in the opposite
/// direction of <paramref name="value"/> in <paramref name="result"/>.
/// </summary>
/// <param name="value">The vector to negate.</param>
/// <param name="result">The vector that the negation of <paramref name="value"/> will be stored in.</param>
public static void Negate(Vector3 value, out Vector3 result)
{
result.mX = -value.mX;
result.mY = -value.mY;
result.mZ = -value.mZ;
}
/// <summary>
/// Creates a new <see cref="Vector3"/> that contains a multiplication of two vectors.
/// </summary>
/// <param name="value1">Source <see cref="Vector3"/>.</param>
/// <param name="value2">Source <see cref="Vector3"/>.</param>
/// <returns>The result of the vector multiplication.</returns>
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);

File diff suppressed because it is too large Load diff

View file

@ -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));
}
}
}

View file

@ -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)

View file

@ -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<uint8> 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<char8*> 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

View file

@ -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

View file

@ -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;
}
/// <summary>
/// Find the current machine's Epsilon for the float data type.
/// (That is, the largest float, e, where e == 0.0f is true.)
/// </summary>
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.
/// <param name="value1">
/// The coordinate on one axis of vertex 1 of the defining triangle.
/// </param>
/// <param name="value2">
/// The coordinate on the same axis of vertex 2 of the defining triangle.
/// </param>
/// <param name="value3">
/// The coordinate on the same axis of vertex 3 of the defining triangle.
/// </param>
/// <param name="amount1">
/// The normalized barycentric (areal) coordinate b2, equal to the weighting factor
/// for vertex 2, the coordinate of which is specified in value2.
/// </param>
/// @param amount2
/// The normalized barycentric (areal) coordinate b3, equal to the weighting factor
/// for vertex 3, the coordinate of which is specified in value3.
/// </param>
/// @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)
)
);
}
}
}

View file

@ -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);

View file

@ -1936,11 +1936,13 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
<ClCompile Include="util\Heap.cpp" />
<ClCompile Include="util\Json.cpp" />
<ClCompile Include="util\MappedFile.cpp" />
<ClCompile Include="util\MathUtils.cpp" />
<ClCompile Include="util\Matrix4.cpp" />
<ClCompile Include="util\PerfTimer.cpp" />
<ClCompile Include="util\Point.cpp" />
<ClCompile Include="util\PolySpline.cpp" />
<ClCompile Include="util\Quaternion.cpp" />
<ClCompile Include="util\Sphere.cpp" />
<ClCompile Include="util\StackHelper.cpp" />
<ClCompile Include="util\String.cpp" />
<ClCompile Include="util\ThreadPool.cpp" />
@ -2166,6 +2168,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
<ClInclude Include="util\Heap.h" />
<ClInclude Include="util\Json.h" />
<ClInclude Include="util\MappedFile.h" />
<ClInclude Include="util\MathUtils.h" />
<ClInclude Include="util\Matrix4.h" />
<ClInclude Include="util\MultiHashSet.h" />
<ClInclude Include="util\PerfTimer.h" />
@ -2176,6 +2179,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
<ClInclude Include="util\Rect.h" />
<ClInclude Include="util\SizedArray.h" />
<ClInclude Include="util\SLIList.h" />
<ClInclude Include="util\Sphere.h" />
<ClInclude Include="util\StackHelper.h" />
<ClInclude Include="util\String.h" />
<ClInclude Include="util\ThreadPool.h" />

View file

@ -713,6 +713,12 @@
<ClCompile Include="util\Json.cpp">
<Filter>src\util</Filter>
</ClCompile>
<ClCompile Include="util\MathUtils.cpp">
<Filter>src\util</Filter>
</ClCompile>
<ClCompile Include="util\Sphere.cpp">
<Filter>src\util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Common.h">
@ -1087,6 +1093,12 @@
<ClInclude Include="util\Json.h">
<Filter>src\util</Filter>
</ClInclude>
<ClInclude Include="util\MathUtils.h">
<Filter>src\util</Filter>
</ClInclude>
<ClInclude Include="util\Sphere.h">
<Filter>src\util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="third_party\libffi\i686-pc-cygwin\src\x86\win32.asm">

View file

@ -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<int> 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<ModelVertex> 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<int32> 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<Vector3> points;
points.Reserve(triWorkList.mSize);
Array<float> centers;
centers.Reserve(triWorkList.mSize);
Array<int> left;
left.Reserve(triWorkList.mSize);
Array<int> 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;
}

View file

@ -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 <vector>
NS_BF_BEGIN;
@ -151,8 +153,6 @@ class ModelMesh
{
public:
String mName;
//String mTexFileName;
//String mBumpFileName;
Array<ModelPrimitives> mPrimitives;
};
@ -166,19 +166,83 @@ public:
Array<ModelNode*> 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<ModelMesh> mMeshes;
Array<ModelJoint> mJoints;
Array<ModelAnimation> mAnims;
Array<ModelNode> mNodes;
Array<ModelMaterialInstance> mMaterials;
Flags mFlags;
AABB mBounds;
Array<ModelBVNode> mBVNodes;
Array<uint16> mBVIndices;
Array<Vector3> mBVVertices;
Array<int32> 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;

View file

@ -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()

View file

@ -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;

View file

@ -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;

View file

@ -95,7 +95,7 @@ typedef std::map<String, DXShaderParam*> 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();

View file

@ -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;
}

View file

@ -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

View file

@ -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;

195
BeefySysLib/util/Sphere.cpp Normal file
View file

@ -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);
}

43
BeefySysLib/util/Sphere.h Normal file
View file

@ -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

View file

@ -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);

View file

@ -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;