Merge remote-tracking branch 'origin/master' into gi

This commit is contained in:
Wojciech Figat
2022-06-20 08:26:06 +02:00
30 changed files with 546 additions and 828 deletions

View File

@@ -50,7 +50,7 @@ namespace FlaxEditor.SceneGraph.Actors
OrientedBoundingBox bounds;
bounds.Extents = Vector3.Half;
bounds.Transformation = world;
world.Decompose(out bounds.Transformation);
normal = -ray.Ray.Direction;
return bounds.Intersects(ref ray.Ray, out distance);

View File

@@ -127,9 +127,15 @@ namespace FlaxEditor.Scripting
return ChannelMask.Red;
if (type.Type == typeof(MaterialSceneTextures))
return MaterialSceneTextures.BaseColor;
if (type.IsValueType)
{
var value = type.CreateInstance();
Utilities.Utils.InitDefaultValues(value);
return value;
}
if (ScriptType.Object.IsAssignableFrom(type))
return null;
if (type.IsValueType || type.CanCreateInstance)
if (type.CanCreateInstance)
{
var value = type.CreateInstance();
Utilities.Utils.InitDefaultValues(value);

View File

@@ -774,9 +774,10 @@ namespace FlaxEditor.Utilities
else
orientation = Quaternion.LookRotation(dir, Vector3.Cross(Vector3.Cross(dir, Vector3.Up), dir));
Vector3 up = Vector3.Up * orientation;
box.Transformation = Matrix.CreateWorld(min + vec * 0.5f, dir, up);
Matrix.Invert(ref box.Transformation, out Matrix inv);
Vector3 vecLocal = Vector3.TransformNormal(vec * 0.5f, inv);
Matrix world = Matrix.CreateWorld(min + vec * 0.5f, dir, up);
world.Decompose(out box.Transformation);
Matrix.Invert(ref world, out Matrix invWorld);
Vector3 vecLocal = Vector3.TransformNormal(vec * 0.5f, invWorld);
box.Extents.X = margin;
box.Extents.Y = margin;
box.Extents.Z = vecLocal.Z;

View File

@@ -34,6 +34,9 @@ namespace FlaxEditor.Windows.Assets
new ScriptType(typeof(ChannelMask)),
new ScriptType(typeof(bool)),
new ScriptType(typeof(int)),
new ScriptType(typeof(Float2)),
new ScriptType(typeof(Float3)),
new ScriptType(typeof(Float4)),
new ScriptType(typeof(Vector2)),
new ScriptType(typeof(Vector3)),
new ScriptType(typeof(Vector4)),

View File

@@ -33,6 +33,9 @@ namespace FlaxEditor.Windows.Assets
new ScriptType(typeof(ChannelMask)),
new ScriptType(typeof(bool)),
new ScriptType(typeof(int)),
new ScriptType(typeof(Float2)),
new ScriptType(typeof(Float3)),
new ScriptType(typeof(Float4)),
new ScriptType(typeof(Vector2)),
new ScriptType(typeof(Vector3)),
new ScriptType(typeof(Vector4)),

View File

@@ -201,6 +201,40 @@ void Matrix3x3::RotationQuaternion(const Quaternion& rotation, Matrix3x3& result
result.M33 = 1.0f - 2.0f * (yy + xx);
}
void Matrix3x3::Decompose(Float3& scale, Matrix3x3& rotation) const
{
// Scaling is the length of the rows
scale = Float3(
Math::Sqrt(M11 * M11 + M12 * M12 + M13 * M13),
Math::Sqrt(M21 * M21 + M22 * M22 + M23 * M23),
Math::Sqrt(M31 * M31 + M32 * M32 + M33 * M33));
// If any of the scaling factors are zero, than the rotation matrix can not exist
rotation = Identity;
if (scale.IsAnyZero())
return;
// Calculate an perfect orthonormal matrix (no reflections)
const auto at = Float3(M31 / scale.Z, M32 / scale.Z, M33 / scale.Z);
const auto up = Float3::Cross(at, Float3(M11 / scale.X, M12 / scale.X, M13 / scale.X));
const auto right = Float3::Cross(up, at);
rotation.SetRight(right);
rotation.SetUp(up);
rotation.SetBackward(at);
// In case of reflexions
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
scale.Y = Float3::Dot(up, GetUp()) > 0.0f ? scale.Y : -scale.Y;
scale.Z = Float3::Dot(at, GetBackward()) > 0.0f ? scale.Z : -scale.Z;
}
void Matrix3x3::Decompose(Float3& scale, Quaternion& rotation) const
{
Matrix3x3 rotationMatrix;
Decompose(scale, rotationMatrix);
Quaternion::RotationMatrix(rotationMatrix, rotation);
}
bool Matrix3x3::operator==(const Matrix3x3& other) const
{
return

View File

@@ -120,6 +120,90 @@ public:
String ToString() const;
public:
// Gets the up Float3 of the matrix; that is M21, M22, and M23.
Float3 GetUp() const
{
return Float3(M21, M22, M23);
}
// Sets Float3 of the matrix; that is M21, M22, and M23.
void SetUp(const Float3& value)
{
M21 = value.X;
M22 = value.Y;
M23 = value.Z;
}
// Gets the down Float3 of the matrix; that is -M21, -M22, and -M23.
Float3 GetDown() const
{
return -Float3(M21, M22, M23);
}
// Sets the down Float3 of the matrix; that is -M21, -M22, and -M23.
void SetDown(const Float3& value)
{
M21 = -value.X;
M22 = -value.Y;
M23 = -value.Z;
}
// Gets the right Float3 of the matrix; that is M11, M12, and M13.
Float3 GetRight() const
{
return Float3(M11, M12, M13);
}
// Sets the right Float3 of the matrix; that is M11, M12, and M13.
void SetRight(const Float3& value)
{
M11 = value.X;
M12 = value.Y;
M13 = value.Z;
}
// Gets the left Float3 of the matrix; that is -M11, -M12, and -M13.
Float3 GetLeft() const
{
return -Float3(M11, M12, M13);
}
// Sets the left Float3 of the matrix; that is -M11, -M12, and -M13.
void SetLeft(const Float3& value)
{
M11 = -value.X;
M12 = -value.Y;
M13 = -value.Z;
}
// Gets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
Float3 GetForward() const
{
return -Float3(M31, M32, M33);
}
// Sets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
void SetForward(const Float3& value)
{
M31 = -value.X;
M32 = -value.Y;
M33 = -value.Z;
}
// Gets the backward Float3 of the matrix; that is M31, M32, and M33.
Float3 GetBackward() const
{
return Float3(M31, M32, M33);
}
// Sets the backward Float3 of the matrix; that is M31, M32, and M33.
void SetBackward(const Float3& value)
{
M31 = value.X;
M32 = value.Y;
M33 = value.Z;
}
// Gets the first row in the matrix; that is M11, M12 and M13.
Float3 GetRow1() const
{
@@ -497,6 +581,22 @@ public:
/// <param name="result">The created rotation matrix.</param>
static void RotationQuaternion(const Quaternion& rotation, Matrix3x3& result);
/// <summary>
/// Decomposes a matrix into a scale and rotation.
/// </summary>
/// <param name="scale">When the method completes, contains the scaling component of the decomposed matrix.</param>
/// <param name="rotation">When the method completes, contains the rotation component of the decomposed matrix.</param>
/// <remarks>This method is designed to decompose an scale-rotation transformation matrix only.</remarks>
void Decompose(Float3& scale, Matrix3x3& rotation) const;
/// <summary>
/// Decomposes a matrix into a scale and rotation.
/// </summary>
/// <param name="scale">When the method completes, contains the scaling component of the decomposed matrix.</param>
/// <param name="rotation">When the method completes, contains the rotation component of the decomposed matrix.</param>
/// <remarks>This method is designed to decompose an scale-rotation transformation matrix only.</remarks>
void Decompose(Float3& scale, Quaternion& rotation) const;
public:
/// <summary>
/// Tests for equality between two objects.

View File

@@ -3,6 +3,7 @@
#include "OrientedBoundingBox.h"
#include "BoundingSphere.h"
#include "BoundingBox.h"
#include "Matrix.h"
#include "Ray.h"
#include "../Types/String.h"
@@ -10,32 +11,41 @@ OrientedBoundingBox::OrientedBoundingBox(const BoundingBox& bb)
{
const Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) * 0.5f;
Extents = bb.Maximum - center;
Matrix::Translation(center, Transformation);
Transformation = ::Transform(center);
}
OrientedBoundingBox::OrientedBoundingBox(const Vector3& extents, const Matrix& transformation)
: Extents(extents)
{
transformation.Decompose(Transformation);
}
OrientedBoundingBox::OrientedBoundingBox(const Vector3& extents, const Matrix3x3& rotationScale, const Vector3& translation)
: Extents(extents)
, Transformation(rotationScale)
, Transformation(translation, rotationScale)
{
Transformation.SetTranslation(translation);
}
OrientedBoundingBox::OrientedBoundingBox(const Vector3& minimum, const Vector3& maximum)
{
const Vector3 center = minimum + (maximum - minimum) * 0.5f;
Extents = maximum - center;
Transformation = ::Transform(center);
}
OrientedBoundingBox::OrientedBoundingBox(Vector3 points[], int32 pointCount)
{
ASSERT(points && pointCount > 0);
Vector3 minimum = points[0];
Vector3 maximum = points[0];
for (int32 i = 1; i < pointCount; i++)
{
Vector3::Min(minimum, points[i], minimum);
Vector3::Max(maximum, points[i], maximum);
}
const Vector3 center = minimum + (maximum - minimum) / 2.0f;
const Vector3 center = minimum + (maximum - minimum) * 0.5f;
Extents = maximum - center;
Matrix::Translation(center, Transformation);
Transformation = ::Transform(center);
}
String OrientedBoundingBox::ToString() const
@@ -45,15 +55,11 @@ String OrientedBoundingBox::ToString() const
void OrientedBoundingBox::GetCorners(Float3 corners[8]) const
{
Float3 xv((float)Extents.X, 0, 0);
Float3 yv(0, (float)Extents.Y, 0);
Float3 zv(0, 0, (float)Extents.Z);
const Float3 xv = Transformation.LocalToWorldVector(Vector3((float)Extents.X, 0, 0));
const Float3 yv = Transformation.LocalToWorldVector(Vector3(0, (float)Extents.Y, 0));
const Float3 zv = Transformation.LocalToWorldVector(Vector3(0, 0, (float)Extents.Z));
Float3::TransformNormal(xv, Transformation, xv);
Float3::TransformNormal(yv, Transformation, yv);
Float3::TransformNormal(zv, Transformation, zv);
const Float3 center = Transformation.GetTranslation();
const Float3 center = Transformation.Translation;
corners[0] = center + xv + yv + zv;
corners[1] = center + xv + yv - zv;
@@ -67,15 +73,11 @@ void OrientedBoundingBox::GetCorners(Float3 corners[8]) const
void OrientedBoundingBox::GetCorners(Double3 corners[8]) const
{
Double3 xv(Extents.X, 0, 0);
Double3 yv(0, Extents.Y, 0);
Double3 zv(0, 0, Extents.Z);
const Double3 xv = Transformation.LocalToWorldVector(Vector3((float)Extents.X, 0, 0));
const Double3 yv = Transformation.LocalToWorldVector(Vector3(0, (float)Extents.Y, 0));
const Double3 zv = Transformation.LocalToWorldVector(Vector3(0, 0, (float)Extents.Z));
Double3::TransformNormal(xv, Transformation, xv);
Double3::TransformNormal(yv, Transformation, yv);
Double3::TransformNormal(zv, Transformation, zv);
const Double3 center = Transformation.GetTranslation();
const Double3 center = Transformation.Translation;
corners[0] = center + xv + yv + zv;
corners[1] = center + xv + yv - zv;
@@ -89,39 +91,26 @@ void OrientedBoundingBox::GetCorners(Double3 corners[8]) const
Vector3 OrientedBoundingBox::GetSize() const
{
Vector3 xv = Vector3(Extents.X * 2, 0, 0);
Vector3 yv = Vector3(0, Extents.Y * 2, 0);
Vector3 zv = Vector3(0, 0, Extents.Z * 2);
Vector3::TransformNormal(xv, Transformation, xv);
Vector3::TransformNormal(yv, Transformation, yv);
Vector3::TransformNormal(zv, Transformation, zv);
const Vector3 xv = Transformation.LocalToWorldVector(Vector3(Extents.X * 2, 0, 0));
const Vector3 yv = Transformation.LocalToWorldVector(Vector3(0, Extents.Y * 2, 0));
const Vector3 zv = Transformation.LocalToWorldVector(Vector3(0, 0, Extents.Z * 2));
return Vector3(xv.Length(), yv.Length(), zv.Length());
}
Vector3 OrientedBoundingBox::GetSizeSquared() const
{
Vector3 xv = Vector3(Extents.X * 2, 0, 0);
Vector3 yv = Vector3(0, Extents.Y * 2, 0);
Vector3 zv = Vector3(0, 0, Extents.Z * 2);
Vector3::TransformNormal(xv, Transformation, xv);
Vector3::TransformNormal(yv, Transformation, yv);
Vector3::TransformNormal(zv, Transformation, zv);
const Vector3 xv = Transformation.LocalToWorldVector(Vector3(Extents.X * 2, 0, 0));
const Vector3 yv = Transformation.LocalToWorldVector(Vector3(0, Extents.Y * 2, 0));
const Vector3 zv = Transformation.LocalToWorldVector(Vector3(0, 0, Extents.Z * 2));
return Vector3(xv.LengthSquared(), yv.LengthSquared(), zv.LengthSquared());
}
BoundingBox OrientedBoundingBox::GetBoundingBox() const
{
BoundingBox result;
Vector3 corners[8];
GetCorners(corners);
BoundingBox::FromPoints(corners, 8, result);
return result;
}
@@ -135,19 +124,21 @@ void OrientedBoundingBox::GetBoundingBox(BoundingBox& result) const
void OrientedBoundingBox::Transform(const Matrix& matrix)
{
const Matrix tmp = Transformation;
Matrix::Multiply(tmp, matrix, Transformation);
::Transform transform;
matrix.Decompose(transform);
Transformation = transform.LocalToWorld(Transformation);
}
void OrientedBoundingBox::Transform(const ::Transform& transform)
{
Transformation = transform.LocalToWorld(Transformation);
}
ContainmentType OrientedBoundingBox::Contains(const Vector3& point, Real* distance) const
{
// Transform the point into the obb coordinates
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
Vector3 locPoint;
Vector3::TransformCoordinate(point, invTrans, locPoint);
Transformation.WorldToLocal(point, locPoint);
locPoint.X = Math::Abs(locPoint.X);
locPoint.Y = Math::Abs(locPoint.Y);
locPoint.Z = Math::Abs(locPoint.Z);
@@ -161,7 +152,7 @@ ContainmentType OrientedBoundingBox::Contains(const Vector3& point, Real* distan
// Transform distance to world space
Vector3 dstVec = Vector3::UnitX * minDstToEdgeLocal;
Vector3::TransformNormal(dstVec, Transformation, dstVec);
Transformation.LocalToWorldVector(dstVec, dstVec);
*distance = dstVec.Length();
}
@@ -173,49 +164,11 @@ ContainmentType OrientedBoundingBox::Contains(const Vector3& point, Real* distan
return ContainmentType::Disjoint;
}
ContainmentType OrientedBoundingBox::Contains(int32 pointsCnt, Vector3 points[]) const
{
ASSERT(pointsCnt > 0);
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
bool containsAll = true;
bool containsAny = false;
for (int32 i = 0; i < pointsCnt; i++)
{
Vector3 locPoint;
Vector3::TransformCoordinate(points[i], invTrans, locPoint);
locPoint.X = Math::Abs(locPoint.X);
locPoint.Y = Math::Abs(locPoint.Y);
locPoint.Z = Math::Abs(locPoint.Z);
// Simple axes-aligned BB check
if (Vector3::NearEqual(locPoint, Extents))
containsAny = true;
if (locPoint.X < Extents.X && locPoint.Y < Extents.Y && locPoint.Z < Extents.Z)
containsAny = true;
else
containsAll = false;
}
if (containsAll)
return ContainmentType::Contains;
if (containsAny)
return ContainmentType::Intersects;
return ContainmentType::Disjoint;
}
ContainmentType OrientedBoundingBox::Contains(const BoundingSphere& sphere, bool ignoreScale) const
{
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
// Transform sphere center into the obb coordinates
Vector3 locCenter;
Vector3::TransformCoordinate(sphere.Center, invTrans, locCenter);
Transformation.WorldToLocal(sphere.Center, locCenter);
Real locRadius;
if (ignoreScale)
@@ -226,7 +179,7 @@ ContainmentType OrientedBoundingBox::Contains(const BoundingSphere& sphere, bool
{
// Transform sphere radius into the obb coordinates
Vector3 vRadius = Vector3::UnitX * sphere.Radius;
Vector3::TransformNormal(vRadius, invTrans, vRadius);
Transformation.LocalToWorldVector(vRadius, vRadius);
locRadius = vRadius.Length();
}
@@ -238,119 +191,25 @@ ContainmentType OrientedBoundingBox::Contains(const BoundingSphere& sphere, bool
if (distance > locRadius * locRadius)
return ContainmentType::Disjoint;
if (minusExtents.X + locRadius <= locCenter.X
&& locCenter.X <= Extents.X - locRadius
&& (Extents.X - minusExtents.X > locRadius
&& minusExtents.Y + locRadius <= locCenter.Y)
&& (locCenter.Y <= Extents.Y - locRadius
&& Extents.Y - minusExtents.Y > locRadius
&& (minusExtents.Z + locRadius <= locCenter.Z
&& locCenter.Z <= Extents.Z - locRadius
&& Extents.Z - minusExtents.Z > locRadius)))
{
if (minusExtents.X + locRadius <= locCenter.X && locCenter.X <= Extents.X - locRadius && (Extents.X - minusExtents.X > locRadius && minusExtents.Y + locRadius <= locCenter.Y) && (locCenter.Y <= Extents.Y - locRadius && Extents.Y - minusExtents.Y > locRadius && (minusExtents.Z + locRadius <= locCenter.Z && locCenter.Z <= Extents.Z - locRadius && Extents.Z - minusExtents.Z > locRadius)))
return ContainmentType::Contains;
}
return ContainmentType::Intersects;
}
/*ContainmentType OrientedBoundingBox::Contains(const OrientedBoundingBox& obb)
{
Vector3 obbCorners[8];
obb.GetCorners(obbCorners);
auto cornersCheck = Contains(8, obbCorners);
if (cornersCheck != ContainmentType::Disjoint)
return cornersCheck;
// http://www.3dkingdoms.com/weekly/bbox.cpp
Vector3 SizeA = Extents;
Vector3 SizeB = obb.Extents;
Vector3 RotA[3];
Vector3 RotB[3];
GetRows(Transformation, RotA);
GetRows(obb.Transformation, RotB);
Matrix R = Matrix::Identity; // Rotation from B to A
Matrix AR = Matrix::Identity; // absolute values of R matrix, to use with box extents
Real ExtentA, ExtentB, Separation;
int i, k;
// Calculate B to A rotation matrix
for (i = 0; i < 3; i++)
{
for (k = 0; k < 3; k++)
{
R[i, k] = Vector3::Dot(RotA[i], RotB[k]);
AR[i, k] = Math::Abs(R[i, k]);
}
}
// Vector separating the centers of Box B and of Box A
Vector3 vSepWS = obb.GetCenter() - GetCenter();
// Rotated into Box A's coordinates
Vector3 vSepA = Vector3(Vector3::Dot(vSepWS, RotA[0]), Vector3::Dot(vSepWS, RotA[1]), Vector3::Dot(vSepWS, RotA[2]));
// Test if any of A's basis vectors separate the box
for (i = 0; i < 3; i++)
{
ExtentA = SizeA[i];
ExtentB = Vector3::Dot(SizeB, Vector3(AR[i, 0], AR[i, 1], AR[i, 2]));
Separation = Math::Abs(vSepA[i]);
if (Separation > ExtentA + ExtentB)
return ContainmentType::Disjoint;
}
// Test if any of B's basis vectors separate the box
for (k = 0; k < 3; k++)
{
ExtentA = Vector3::Dot(SizeA, Vector3(AR[0, k], AR[1, k], AR[2, k]));
ExtentB = SizeB[k];
Separation = Math::Abs(Vector3::Dot(vSepA, Vector3(R[0, k], R[1, k], R[2, k])));
if (Separation > ExtentA + ExtentB)
return ContainmentType::Disjoint;
}
// Now test Cross Products of each basis vector combination ( A[i], B[k] )
for (i = 0; i < 3; i++)
{
for (k = 0; k < 3; k++)
{
int i1 = (i + 1) % 3, i2 = (i + 2) % 3;
int k1 = (k + 1) % 3, k2 = (k + 2) % 3;
ExtentA = SizeA[i1] * AR[i2, k] + SizeA[i2] * AR[i1, k];
ExtentB = SizeB[k1] * AR[i, k2] + SizeB[k2] * AR[i, k1];
Separation = Math::Abs(vSepA[i2] * R[i1, k] - vSepA[i1] * R[i2, k]);
if (Separation > ExtentA + ExtentB)
return ContainmentType::Disjoint;
}
}
// No separating axis found, the boxes overlap
return ContainmentType::Intersects;
}*/
bool OrientedBoundingBox::Intersects(const Ray& ray, Vector3& point) const
{
// Put ray in box space
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
Ray bRay;
Vector3::TransformNormal(ray.Direction, invTrans, bRay.Direction);
Vector3::TransformCoordinate(ray.Position, invTrans, bRay.Position);
Transformation.WorldToLocalVector(ray.Direction, bRay.Direction);
Transformation.WorldToLocal(ray.Position, bRay.Position);
// Perform a regular ray to BoundingBox check
const BoundingBox bb = BoundingBox(-Extents, Extents);
const BoundingBox bb(-Extents, Extents);
const bool intersects = CollisionsHelper::RayIntersectsBox(bRay, bb, point);
// Put the result intersection back to world
if (intersects)
Vector3::TransformCoordinate(point, Transformation, point);
Transformation.LocalToWorld(point, point);
return intersects;
}
@@ -366,19 +225,16 @@ bool OrientedBoundingBox::Intersects(const Ray& ray, Real& distance) const
bool OrientedBoundingBox::Intersects(const Ray& ray, Real& distance, Vector3& normal) const
{
// Put ray in box space
Matrix invTrans;
Matrix::Invert(Transformation, invTrans);
Ray bRay;
Vector3::TransformNormal(ray.Direction, invTrans, bRay.Direction);
Vector3::TransformCoordinate(ray.Position, invTrans, bRay.Position);
Transformation.WorldToLocalVector(ray.Direction, bRay.Direction);
Transformation.WorldToLocal(ray.Position, bRay.Position);
// Perform a regular ray to BoundingBox check
const BoundingBox bb(-Extents, Extents);
if (CollisionsHelper::RayIntersectsBox(bRay, bb, distance, normal))
{
// Put the result intersection back to world
Vector3::TransformNormal(normal, Transformation, normal);
Transformation.LocalToWorldVector(normal, normal);
normal.Normalize();
return true;
}

View File

@@ -29,32 +29,16 @@ using Real = System.Single;
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace FlaxEngine
{
/// <summary>
/// OrientedBoundingBox (OBB) is a rectangular block, much like an AABB (BoundingBox) but with an arbitrary orientation.
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct OrientedBoundingBox : IEquatable<OrientedBoundingBox>, IFormattable
partial struct OrientedBoundingBox : IEquatable<OrientedBoundingBox>, IFormattable
{
/// <summary>
/// Half lengths of the box along each axis.
/// </summary>
public Vector3 Extents;
/// <summary>
/// The matrix which aligns and scales the box, and its translation vector represents the center of the box.
/// </summary>
public Matrix Transformation;
/// <summary>
/// Creates an <see cref="OrientedBoundingBox" /> from a BoundingBox.
/// </summary>
@@ -62,9 +46,9 @@ namespace FlaxEngine
/// <remarks>Initially, the OBB is axis-aligned box, but it can be rotated and transformed later.</remarks>
public OrientedBoundingBox(BoundingBox bb)
{
Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) / 2f;
Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) * 0.5f;
Extents = bb.Maximum - center;
Transformation = Matrix.Translation(center);
Transformation = new Transform(center);
}
/// <summary>
@@ -75,7 +59,7 @@ namespace FlaxEngine
public OrientedBoundingBox(Vector3 extents, Matrix transformation)
{
Extents = extents;
Transformation = transformation;
transformation.Decompose(out Transformation);
}
/// <summary>
@@ -86,9 +70,9 @@ namespace FlaxEngine
/// <remarks>Initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.</remarks>
public OrientedBoundingBox(Vector3 minimum, Vector3 maximum)
{
Vector3 center = minimum + (maximum - minimum) / 2f;
Vector3 center = minimum + (maximum - minimum) * 0.5f;
Extents = maximum - center;
Transformation = Matrix.Translation(center);
Transformation = new Transform(center);
}
/// <summary>
@@ -100,19 +84,16 @@ namespace FlaxEngine
{
if ((points == null) || (points.Length == 0))
throw new ArgumentNullException(nameof(points));
var minimum = Vector3.Maximum;
var maximum = Vector3.Minimum;
for (var i = 0; i < points.Length; ++i)
var minimum = points[0];
var maximum = points[0];
for (var i = 1; i < points.Length; ++i)
{
Vector3.Min(ref minimum, ref points[i], out minimum);
Vector3.Max(ref maximum, ref points[i], out maximum);
}
Vector3 center = minimum + (maximum - minimum) / 2f;
Vector3 center = minimum + (maximum - minimum) * 0.5f;
Extents = maximum - center;
Transformation = Matrix.Translation(center);
Transformation = new Transform(center);
}
/// <summary>
@@ -121,25 +102,8 @@ namespace FlaxEngine
/// <returns>An array of points representing the eight corners of the bounding box.</returns>
public Vector3[] GetCorners()
{
var xv = new Vector3(Extents.X, 0, 0);
var yv = new Vector3(0, Extents.Y, 0);
var zv = new Vector3(0, 0, Extents.Z);
Vector3.TransformNormal(ref xv, ref Transformation, out xv);
Vector3.TransformNormal(ref yv, ref Transformation, out yv);
Vector3.TransformNormal(ref zv, ref Transformation, out zv);
Vector3 center = Transformation.TranslationVector;
var corners = new Vector3[8];
corners[0] = center + xv + yv + zv;
corners[1] = center + xv + yv - zv;
corners[2] = center - xv + yv - zv;
corners[3] = center - xv + yv + zv;
corners[4] = center + xv - yv + zv;
corners[5] = center + xv - yv - zv;
corners[6] = center - xv - yv - zv;
corners[7] = center - xv - yv + zv;
GetCorners(corners);
return corners;
}
@@ -147,28 +111,12 @@ namespace FlaxEngine
/// Retrieves the eight corners of the bounding box.
/// </summary>
/// <param name="corners">An array of points representing the eight corners of the bounding box.</param>
public void GetCorners(Vector3[] corners)
public unsafe void GetCorners(Vector3[] corners)
{
if (corners == null || corners.Length != 8)
throw new ArgumentException();
var xv = new Vector3(Extents.X, 0, 0);
var yv = new Vector3(0, Extents.Y, 0);
var zv = new Vector3(0, 0, Extents.Z);
Vector3.TransformNormal(ref xv, ref Transformation, out xv);
Vector3.TransformNormal(ref yv, ref Transformation, out yv);
Vector3.TransformNormal(ref zv, ref Transformation, out zv);
Vector3 center = Transformation.TranslationVector;
corners[0] = center + xv + yv + zv;
corners[1] = center + xv + yv - zv;
corners[2] = center - xv + yv - zv;
corners[3] = center - xv + yv + zv;
corners[4] = center + xv - yv + zv;
corners[5] = center + xv - yv - zv;
corners[6] = center - xv - yv - zv;
corners[7] = center - xv - yv + zv;
fixed (Vector3* ptr = corners)
GetCorners(ptr);
}
/// <summary>
@@ -177,14 +125,10 @@ namespace FlaxEngine
/// <param name="corners">An array of points representing the eight corners of the bounding box.</param>
public unsafe void GetCorners(Vector3* corners)
{
var xv = new Vector3(Extents.X, 0, 0);
var yv = new Vector3(0, Extents.Y, 0);
var zv = new Vector3(0, 0, Extents.Z);
Vector3.TransformNormal(ref xv, ref Transformation, out xv);
Vector3.TransformNormal(ref yv, ref Transformation, out yv);
Vector3.TransformNormal(ref zv, ref Transformation, out zv);
Vector3 center = Transformation.TranslationVector;
Vector3 xv = Transformation.LocalToWorldVector(new Vector3(Extents.X, 0, 0));
Vector3 yv = Transformation.LocalToWorldVector(new Vector3(0, Extents.Y, 0));
Vector3 zv = Transformation.LocalToWorldVector(new Vector3(0, 0, Extents.Z));
Vector3 center = Transformation.Translation;
corners[0] = center + xv + yv + zv;
corners[1] = center + xv + yv - zv;
@@ -204,55 +148,32 @@ namespace FlaxEngine
{
if (corners == null)
throw new ArgumentNullException();
var xv = new Vector3(Extents.X, 0, 0);
var yv = new Vector3(0, Extents.Y, 0);
var zv = new Vector3(0, 0, Extents.Z);
Vector3.TransformNormal(ref xv, ref Transformation, out xv);
Vector3.TransformNormal(ref yv, ref Transformation, out yv);
Vector3.TransformNormal(ref zv, ref Transformation, out zv);
Vector3 center = Transformation.TranslationVector;
corners.Add(center + xv + yv + zv);
corners.Add(center + xv + yv - zv);
corners.Add(center - xv + yv - zv);
corners.Add(center - xv + yv + zv);
corners.Add(center + xv - yv + zv);
corners.Add(center + xv - yv - zv);
corners.Add(center - xv - yv - zv);
corners.Add(center - xv - yv + zv);
corners.AddRange(GetCorners());
}
/// <summary>
/// Transforms this box using a transformation matrix.
/// </summary>
/// <param name="mat">The transformation matrix.</param>
/// <remarks>
/// While any kind of transformation can be applied, it is recommended to apply scaling using scale method instead, which
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection accuracy.
/// </remarks>
/// <remarks>While any kind of transformation can be applied, it is recommended to apply scaling using scale method instead, which scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection accuracy.</remarks>
public void Transform(ref Matrix mat)
{
Transformation *= mat;
mat.Decompose(out var transform);
Transformation = transform.LocalToWorld(Transformation);
}
/// <summary>
/// Transforms this box using a transformation matrix.
/// </summary>
/// <param name="mat">The transformation matrix.</param>
/// <remarks>
/// While any kind of transformation can be applied, it is recommended to apply scaling using scale method instead, which
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection accuracy.
/// </remarks>
/// <remarks>While any kind of transformation can be applied, it is recommended to apply scaling using scale method instead, which scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection accuracy.</remarks>
public void Transform(Matrix mat)
{
Transformation *= mat;
Transform(ref mat);
}
/// <summary>
/// Scales the <see cref="OrientedBoundingBox" /> by scaling its Extents without affecting the Transformation matrix,
/// By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
/// Scales the <see cref="OrientedBoundingBox" /> by scaling its Extents without affecting the Transformation matrix, By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
/// </summary>
/// <param name="scaling"></param>
public void Scale(ref Vector3 scaling)
@@ -261,8 +182,7 @@ namespace FlaxEngine
}
/// <summary>
/// Scales the <see cref="OrientedBoundingBox" /> by scaling its Extents without affecting the Transformation matrix,
/// By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
/// Scales the <see cref="OrientedBoundingBox" /> by scaling its Extents without affecting the Transformation matrix, By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
/// </summary>
/// <param name="scaling"></param>
public void Scale(Vector3 scaling)
@@ -271,8 +191,7 @@ namespace FlaxEngine
}
/// <summary>
/// Scales the <see cref="OrientedBoundingBox" /> by scaling its Extents without affecting the Transformation matrix,
/// By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
/// Scales the <see cref="OrientedBoundingBox" /> by scaling its Extents without affecting the Transformation matrix, By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
/// </summary>
/// <param name="scaling"></param>
public void Scale(Real scaling)
@@ -286,7 +205,7 @@ namespace FlaxEngine
/// <param name="translation">the translation vector.</param>
public void Translate(ref Vector3 translation)
{
Transformation.TranslationVector += translation;
Transformation.Translation += translation;
}
/// <summary>
@@ -295,14 +214,13 @@ namespace FlaxEngine
/// <param name="translation">the translation vector.</param>
public void Translate(Vector3 translation)
{
Transformation.TranslationVector += translation;
Transformation.Translation += translation;
}
/// <summary>
/// The size of the <see cref="OrientedBoundingBox" /> if no scaling is applied to the transformation matrix.
/// </summary>
/// <remarks>The property will return the actual size even if the scaling is applied using Scale method, but if the scaling is applied to transformation matrix, use GetSize Function instead.
/// </remarks>
/// <remarks>The property will return the actual size even if the scaling is applied using Scale method, but if the scaling is applied to transformation matrix, use GetSize Function instead.</remarks>
public Vector3 Size => Extents * 2;
/// <summary>
@@ -312,13 +230,9 @@ namespace FlaxEngine
/// <remarks>This method is computationally expensive, so if no scale is applied to the transformation matrix use <see cref="OrientedBoundingBox.Size" /> property instead.</remarks>
public Vector3 GetSize()
{
var xv = new Vector3(Extents.X * 2, 0, 0);
var yv = new Vector3(0, Extents.Y * 2, 0);
var zv = new Vector3(0, 0, Extents.Z * 2);
Vector3.TransformNormal(ref xv, ref Transformation, out xv);
Vector3.TransformNormal(ref yv, ref Transformation, out yv);
Vector3.TransformNormal(ref zv, ref Transformation, out zv);
Vector3 xv = Transformation.LocalToWorldVector(new Vector3(Extents.X * 2, 0, 0));
Vector3 yv = Transformation.LocalToWorldVector(new Vector3(0, Extents.Y * 2, 0));
Vector3 zv = Transformation.LocalToWorldVector(new Vector3(0, 0, Extents.Z * 2));
return new Vector3(xv.Length, yv.Length, zv.Length);
}
@@ -328,20 +242,16 @@ namespace FlaxEngine
/// <returns>The size of the consideration</returns>
public Vector3 GetSizeSquared()
{
var xv = new Vector3(Extents.X * 2, 0, 0);
var yv = new Vector3(0, Extents.Y * 2, 0);
var zv = new Vector3(0, 0, Extents.Z * 2);
Vector3.TransformNormal(ref xv, ref Transformation, out xv);
Vector3.TransformNormal(ref yv, ref Transformation, out yv);
Vector3.TransformNormal(ref zv, ref Transformation, out zv);
Vector3 xv = Transformation.LocalToWorldVector(new Vector3(Extents.X * 2, 0, 0));
Vector3 yv = Transformation.LocalToWorldVector(new Vector3(0, Extents.Y * 2, 0));
Vector3 zv = Transformation.LocalToWorldVector(new Vector3(0, 0, Extents.Z * 2));
return new Vector3(xv.LengthSquared, yv.LengthSquared, zv.LengthSquared);
}
/// <summary>
/// Returns the center of the <see cref="OrientedBoundingBox" />.
/// </summary>
public Vector3 Center => Transformation.TranslationVector;
public Vector3 Center => Transformation.Translation;
/// <summary>
/// Determines whether a <see cref="OrientedBoundingBox" /> contains a point.
@@ -351,10 +261,7 @@ namespace FlaxEngine
public ContainmentType Contains(ref Vector3 point)
{
// Transform the point into the obb coordinates
Matrix.Invert(ref Transformation, out Matrix invTrans);
Vector3.TransformCoordinate(ref point, ref invTrans, out Vector3 locPoint);
Transformation.WorldToLocal(ref point, out Vector3 locPoint);
locPoint.X = Math.Abs(locPoint.X);
locPoint.Y = Math.Abs(locPoint.Y);
locPoint.Z = Math.Abs(locPoint.Z);
@@ -377,47 +284,6 @@ namespace FlaxEngine
return Contains(ref point);
}
/// <summary>
/// Determines whether a <see cref="OrientedBoundingBox" /> contains an array of points>.
/// </summary>
/// <param name="points">The points array to test.</param>
/// <returns>The type of containment.</returns>
public ContainmentType Contains(Vector3[] points)
{
Matrix.Invert(ref Transformation, out Matrix invTrans);
var containsAll = true;
var containsAny = false;
for (var i = 0; i < points.Length; i++)
{
Vector3.TransformCoordinate(ref points[i], ref invTrans, out Vector3 locPoint);
locPoint.X = Math.Abs(locPoint.X);
locPoint.Y = Math.Abs(locPoint.Y);
locPoint.Z = Math.Abs(locPoint.Z);
// Simple axes-aligned BB check
if (Mathf.NearEqual(locPoint.X, Extents.X) &&
Mathf.NearEqual(locPoint.Y, Extents.Y) &&
Mathf.NearEqual(locPoint.Z, Extents.Z))
containsAny = true;
if ((locPoint.X < Extents.X) && (locPoint.Y < Extents.Y) && (locPoint.Z < Extents.Z))
containsAny = true;
else
containsAll = false;
}
if (containsAll)
return ContainmentType.Contains;
if (containsAny)
return ContainmentType.Intersects;
return ContainmentType.Disjoint;
}
/// <summary>
/// Determines whether a <see cref="OrientedBoundingBox" /> contains a <see cref="BoundingSphere" />.
/// </summary>
@@ -427,10 +293,8 @@ namespace FlaxEngine
/// <remarks>This method is not designed for <see cref="OrientedBoundingBox" /> which has a non-uniform scaling applied to its transformation matrix. But any type of scaling applied using Scale method will keep this method accurate.</remarks>
public ContainmentType Contains(BoundingSphere sphere, bool ignoreScale = false)
{
Matrix.Invert(ref Transformation, out Matrix invTrans);
// Transform sphere center into the obb coordinates
Vector3.TransformCoordinate(ref sphere.Center, ref invTrans, out Vector3 locCenter);
Transformation.WorldToLocal(ref sphere.Center, out Vector3 locCenter);
Real locRadius;
if (ignoreScale)
@@ -439,235 +303,19 @@ namespace FlaxEngine
{
// Transform sphere radius into the obb coordinates
Vector3 vRadius = Vector3.UnitX * sphere.Radius;
Vector3.TransformNormal(ref vRadius, ref invTrans, out vRadius);
Transformation.LocalToWorldVector(ref vRadius, out vRadius);
locRadius = vRadius.Length;
}
//Perform regular BoundingBox to BoundingSphere containment check
// Perform regular BoundingBox to BoundingSphere containment check
Vector3 minusExtens = -Extents;
Vector3.Clamp(ref locCenter, ref minusExtens, ref Extents, out Vector3 vector);
Real distance = Vector3.DistanceSquared(ref locCenter, ref vector);
if (distance > locRadius * locRadius)
return ContainmentType.Disjoint;
if ((minusExtens.X + locRadius <= locCenter.X) && (locCenter.X <= Extents.X - locRadius) && (Extents.X - minusExtens.X > locRadius) && (minusExtens.Y + locRadius <= locCenter.Y) && (locCenter.Y <= Extents.Y - locRadius) && (Extents.Y - minusExtens.Y > locRadius) && (minusExtens.Z + locRadius <= locCenter.Z) && (locCenter.Z <= Extents.Z - locRadius) && (Extents.Z - minusExtens.Z > locRadius))
return ContainmentType.Contains;
return ContainmentType.Intersects;
}
private static Float3[] GetRows(ref Matrix mat)
{
return new[]
{
new Float3(mat.M11, mat.M12, mat.M13),
new Float3(mat.M21, mat.M22, mat.M23),
new Float3(mat.M31, mat.M32, mat.M33)
};
}
/// <summary>
/// Check the intersection between two <see cref="OrientedBoundingBox" />
/// </summary>
/// <param name="obb">The OrientedBoundingBox to test.</param>
/// <returns>The type of containment the two objects have.</returns>
/// <remarks>For accuracy, The transformation matrix for both <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
public ContainmentType Contains(ref OrientedBoundingBox obb)
{
ContainmentType cornersCheck = Contains(obb.GetCorners());
if (cornersCheck != ContainmentType.Disjoint)
return cornersCheck;
//http://www.3dkingdoms.com/weekly/bbox.cpp
Vector3 SizeA = Extents;
Vector3 SizeB = obb.Extents;
var RotA = GetRows(ref Transformation);
var RotB = GetRows(ref obb.Transformation);
var R = new Matrix(); // Rotation from B to A
var AR = new Matrix(); // absolute values of R matrix, to use with box extents
Real ExtentA, ExtentB, Separation;
int i, k;
// Calculate B to A rotation matrix
for (i = 0; i < 3; i++)
for (k = 0; k < 3; k++)
{
R[i, k] = Float3.Dot(RotA[i], RotB[k]);
AR[i, k] = Math.Abs(R[i, k]);
}
// Vector separating the centers of Box B and of Box A
Vector3 vSepWS = obb.Center - Center;
// Rotated into Box A's coordinates
var vSepA = new Vector3(Vector3.Dot(vSepWS, RotA[0]), Vector3.Dot(vSepWS, RotA[1]), Vector3.Dot(vSepWS, RotA[2]));
// Test if any of A's basis vectors separate the box
for (i = 0; i < 3; i++)
{
ExtentA = SizeA[i];
ExtentB = Vector3.Dot(SizeB, new Vector3(AR[i, 0], AR[i, 1], AR[i, 2]));
Separation = Math.Abs(vSepA[i]);
if (Separation > ExtentA + ExtentB)
return ContainmentType.Disjoint;
}
// Test if any of B's basis vectors separate the box
for (k = 0; k < 3; k++)
{
ExtentA = Vector3.Dot(SizeA, new Vector3(AR[0, k], AR[1, k], AR[2, k]));
ExtentB = SizeB[k];
Separation = Math.Abs(Vector3.Dot(vSepA, new Vector3(R[0, k], R[1, k], R[2, k])));
if (Separation > ExtentA + ExtentB)
return ContainmentType.Disjoint;
}
// Now test Cross Products of each basis vector combination ( A[i], B[k] )
for (i = 0; i < 3; i++)
for (k = 0; k < 3; k++)
{
int i1 = (i + 1) % 3, i2 = (i + 2) % 3;
int k1 = (k + 1) % 3, k2 = (k + 2) % 3;
ExtentA = SizeA[i1] * AR[i2, k] + SizeA[i2] * AR[i1, k];
ExtentB = SizeB[k1] * AR[i, k2] + SizeB[k2] * AR[i, k1];
Separation = Math.Abs(vSepA[i2] * R[i1, k] - vSepA[i1] * R[i2, k]);
if (Separation > ExtentA + ExtentB)
return ContainmentType.Disjoint;
}
// No separating axis found, the boxes overlap
return ContainmentType.Intersects;
}
/// <summary>
/// Check the intersection between an <see cref="OrientedBoundingBox" /> and a line defined by two points
/// </summary>
/// <param name="L1">The first point in the line.</param>
/// <param name="L2">The second point in the line.</param>
/// <returns>The type of containment the two objects have.</returns>
/// <remarks>For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
public ContainmentType ContainsLine(ref Vector3 L1, ref Vector3 L2)
{
ContainmentType cornersCheck = Contains(new[]
{
L1,
L2
});
if (cornersCheck != ContainmentType.Disjoint)
return cornersCheck;
//http://www.3dkingdoms.com/weekly/bbox.cpp
// Put line in box space
Matrix.Invert(ref Transformation, out Matrix invTrans);
Vector3.TransformCoordinate(ref L1, ref invTrans, out Vector3 LB1);
Vector3.TransformCoordinate(ref L1, ref invTrans, out Vector3 LB2);
// Get line midpoint and extent
Vector3 LMid = (LB1 + LB2) * 0.5f;
Vector3 L = LB1 - LMid;
var LExt = new Vector3(Math.Abs(L.X), Math.Abs(L.Y), Math.Abs(L.Z));
// Use Separating Axis Test
// Separation vector from box center to line center is LMid, since the line is in box space
if (Math.Abs(LMid.X) > Extents.X + LExt.X)
return ContainmentType.Disjoint;
if (Math.Abs(LMid.Y) > Extents.Y + LExt.Y)
return ContainmentType.Disjoint;
if (Math.Abs(LMid.Z) > Extents.Z + LExt.Z)
return ContainmentType.Disjoint;
// Cross products of line and each axis
if (Math.Abs(LMid.Y * L.Z - LMid.Z * L.Y) > Extents.Y * LExt.Z + Extents.Z * LExt.Y)
return ContainmentType.Disjoint;
if (Math.Abs(LMid.X * L.Z - LMid.Z * L.X) > Extents.X * LExt.Z + Extents.Z * LExt.X)
return ContainmentType.Disjoint;
if (Math.Abs(LMid.X * L.Y - LMid.Y * L.X) > Extents.X * LExt.Y + Extents.Y * LExt.X)
return ContainmentType.Disjoint;
// No separating axis, the line intersects
return ContainmentType.Intersects;
}
/// <summary>
/// Check the intersection between an <see cref="OrientedBoundingBox" /> and <see cref="BoundingBox" />
/// </summary>
/// <param name="box">The BoundingBox to test.</param>
/// <returns>The type of containment the two objects have.</returns>
/// <remarks>For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
public ContainmentType Contains(ref BoundingBox box)
{
ContainmentType cornersCheck = Contains(box.GetCorners());
if (cornersCheck != ContainmentType.Disjoint)
return cornersCheck;
Vector3 boxCenter = box.Minimum + (box.Maximum - box.Minimum) / 2f;
Vector3 boxExtents = box.Maximum - boxCenter;
Vector3 SizeA = Extents;
Vector3 SizeB = boxExtents;
var RotA = GetRows(ref Transformation);
Real ExtentA, ExtentB, Separation;
int i, k;
// Rotation from B to A
Matrix.Invert(ref Transformation, out Matrix R);
var AR = new Matrix(); // absolute values of R matrix, to use with box extents
for (i = 0; i < 3; i++)
for (k = 0; k < 3; k++)
AR[i, k] = Math.Abs(R[i, k]);
// Vector separating the centers of Box B and of Box A
Vector3 vSepWS = boxCenter - Center;
// Rotated into Box A's coordinates
var vSepA = new Vector3(Vector3.Dot(vSepWS, RotA[0]), Vector3.Dot(vSepWS, RotA[1]), Vector3.Dot(vSepWS, RotA[2]));
// Test if any of A's basis vectors separate the box
for (i = 0; i < 3; i++)
{
ExtentA = SizeA[i];
ExtentB = Vector3.Dot(SizeB, new Vector3(AR[i, 0], AR[i, 1], AR[i, 2]));
Separation = Math.Abs(vSepA[i]);
if (Separation > ExtentA + ExtentB)
return ContainmentType.Disjoint;
}
// Test if any of B's basis vectors separate the box
for (k = 0; k < 3; k++)
{
ExtentA = Vector3.Dot(SizeA, new Vector3(AR[0, k], AR[1, k], AR[2, k]));
ExtentB = SizeB[k];
Separation = Math.Abs(Vector3.Dot(vSepA, new Vector3(R[0, k], R[1, k], R[2, k])));
if (Separation > ExtentA + ExtentB)
return ContainmentType.Disjoint;
}
// Now test Cross Products of each basis vector combination ( A[i], B[k] )
for (i = 0; i < 3; i++)
for (k = 0; k < 3; k++)
{
int i1 = (i + 1) % 3, i2 = (i + 2) % 3;
int k1 = (k + 1) % 3, k2 = (k + 2) % 3;
ExtentA = SizeA[i1] * AR[i2, k] + SizeA[i2] * AR[i1, k];
ExtentB = SizeB[k1] * AR[i, k2] + SizeB[k2] * AR[i, k1];
Separation = Math.Abs(vSepA[i2] * R[i1, k] - vSepA[i1] * R[i2, k]);
if (Separation > ExtentA + ExtentB)
return ContainmentType.Disjoint;
}
// No separating axis found, the boxes overlap
return ContainmentType.Intersects;
}
@@ -680,11 +328,9 @@ namespace FlaxEngine
public bool Intersects(ref Ray ray, out Vector3 point)
{
// Put ray in box space
Matrix.Invert(ref Transformation, out Matrix invTrans);
Ray bRay;
Vector3.TransformNormal(ref ray.Direction, ref invTrans, out bRay.Direction);
Vector3.TransformCoordinate(ref ray.Position, ref invTrans, out bRay.Position);
Transformation.WorldToLocalVector(ref ray.Direction, out bRay.Direction);
Transformation.WorldToLocal(ref ray.Position, out bRay.Position);
// Perform a regular ray to BoundingBox check
var bb = new BoundingBox(-Extents, Extents);
@@ -692,7 +338,7 @@ namespace FlaxEngine
// Put the result intersection back to world
if (intersects)
Vector3.TransformCoordinate(ref point, ref Transformation, out point);
Transformation.LocalToWorld(ref point, out point);
return intersects;
}
@@ -724,25 +370,6 @@ namespace FlaxEngine
return Intersects(ref ray, out Vector3 _);
}
private Vector3[] GetLocalCorners()
{
var xv = new Vector3(Extents.X, 0, 0);
var yv = new Vector3(0, Extents.Y, 0);
var zv = new Vector3(0, 0, Extents.Z);
var corners = new Vector3[8];
corners[0] = +xv + yv + zv;
corners[1] = +xv + yv - zv;
corners[2] = -xv + yv - zv;
corners[3] = -xv + yv + zv;
corners[4] = +xv - yv + zv;
corners[5] = +xv - yv - zv;
corners[6] = -xv - yv - zv;
corners[7] = -xv - yv + zv;
return corners;
}
/// <summary>
/// Get the axis-aligned <see cref="BoundingBox" /> which contains all <see cref="OrientedBoundingBox" /> corners.
/// </summary>
@@ -752,94 +379,6 @@ namespace FlaxEngine
return BoundingBox.FromPoints(GetCorners());
}
/// <summary>
/// Calculates the matrix required to transfer any point from one <see cref="OrientedBoundingBox" /> local coordinates to another.
/// </summary>
/// <param name="a">The source OrientedBoundingBox.</param>
/// <param name="b">The target OrientedBoundingBox.</param>
/// <param name="noMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
/// <returns>The matrix.</returns>
public static Matrix GetBoxToBoxMatrix(ref OrientedBoundingBox a, ref OrientedBoundingBox b, bool noMatrixScaleApplied = false)
{
Matrix AtoB_Matrix;
// Calculate B to A transformation matrix
if (noMatrixScaleApplied)
{
var RotA = GetRows(ref a.Transformation);
var RotB = GetRows(ref b.Transformation);
AtoB_Matrix = new Matrix();
int i, k;
for (i = 0; i < 3; i++)
for (k = 0; k < 3; k++)
AtoB_Matrix[i, k] = Float3.Dot(RotB[i], RotA[k]);
Vector3 v = b.Center - a.Center;
AtoB_Matrix.M41 = Float3.Dot(v, RotA[0]);
AtoB_Matrix.M42 = Float3.Dot(v, RotA[1]);
AtoB_Matrix.M43 = Float3.Dot(v, RotA[2]);
AtoB_Matrix.M44 = 1;
}
else
{
Matrix.Invert(ref a.Transformation, out Matrix AInvMat);
AtoB_Matrix = b.Transformation * AInvMat;
}
return AtoB_Matrix;
}
/// <summary>
/// Merge an OrientedBoundingBox B into another OrientedBoundingBox A, by expanding A to contain B and keeping A orientation.
/// </summary>
/// <param name="a">The <see cref="OrientedBoundingBox" /> to merge into it.</param>
/// <param name="b">The <see cref="OrientedBoundingBox" /> to be merged</param>
/// <param name="noMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
/// <remarks>Unlike merging axis aligned boxes, The operation is not interchangeable, because it keeps A orientation and merge B into it.</remarks>
public static void Merge(ref OrientedBoundingBox a, ref OrientedBoundingBox b, bool noMatrixScaleApplied = false)
{
Matrix AtoB_Matrix = GetBoxToBoxMatrix(ref a, ref b, noMatrixScaleApplied);
//Get B corners in A Space
Vector3[] bCorners = b.GetLocalCorners();
for (int i = 0; i < bCorners.Length; i++)
Vector3.TransformCoordinate(ref bCorners[i], ref AtoB_Matrix, out bCorners[i]);
//Get A local Bounding Box
var A_LocalBB = new BoundingBox(-a.Extents, a.Extents);
//Find B BoundingBox in A Space
BoundingBox B_LocalBB = BoundingBox.FromPoints(bCorners);
//Merger A and B local Bounding Boxes
BoundingBox.Merge(ref B_LocalBB, ref A_LocalBB, out BoundingBox mergedBB);
//Find the new Extents and Center, Transform Center back to world
Vector3 newCenter = mergedBB.Minimum + (mergedBB.Maximum - mergedBB.Minimum) / 2f;
a.Extents = mergedBB.Maximum - newCenter;
Vector3.TransformCoordinate(ref newCenter, ref a.Transformation, out newCenter);
a.Transformation.TranslationVector = newCenter;
}
/// <summary>
/// Merge this OrientedBoundingBox into another OrientedBoundingBox, keeping the other OrientedBoundingBox orientation.
/// </summary>
/// <param name="oob">The other <see cref="OrientedBoundingBox" /> to merge into.</param>
/// <param name="noMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
public void MergeInto(ref OrientedBoundingBox oob, bool noMatrixScaleApplied = false)
{
Merge(ref oob, ref this, noMatrixScaleApplied);
}
/// <summary>
/// Merge another OrientedBoundingBox into this OrientedBoundingBox.
/// </summary>
/// <param name="oob">The other <see cref="OrientedBoundingBox" /> to merge into this OrientedBoundingBox.</param>
/// <param name="noMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
public void Add(ref OrientedBoundingBox oob, bool noMatrixScaleApplied = false)
{
Merge(ref this, ref oob, noMatrixScaleApplied);
}
/// <summary>
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
/// </summary>

View File

@@ -3,19 +3,26 @@
#pragma once
#include "Vector3.h"
#include "Matrix.h"
#include "CollisionsHelper.h"
#include "Transform.h"
// Oriented Bounding Box (OBB) is a rectangular block, much like an AABB (Bounding Box) but with an arbitrary orientation in 3D space.
API_STRUCT(InBuild) struct FLAXENGINE_API OrientedBoundingBox
enum class ContainmentType;
/// <summary>
/// Oriented Bounding Box (OBB) is a rectangular block, much like an AABB (Bounding Box) but with an arbitrary orientation in 3D space.
/// </summary>
API_STRUCT() struct FLAXENGINE_API OrientedBoundingBox
{
public:
// Half lengths of the box along each axis.
Vector3 Extents;
DECLARE_SCRIPTING_TYPE_MINIMAL(OrientedBoundingBox);
// The matrix which aligns and scales the box, and its translation vector represents the center of the box.
Matrix Transformation;
/// <summary>
/// Half lengths of the box along each axis.
/// </summary>
API_FIELD() Vector3 Extents;
/// <summary>
/// The transformation which aligns and scales the box, and its translation vector represents the center of the box.
/// </summary>
API_FIELD() Transform Transformation;
public:
/// <summary>
@@ -25,34 +32,16 @@ public:
{
}
// Init
// @param bb The BoundingBox to create from
OrientedBoundingBox(const BoundingBox& bb);
// Init
// @param extents The half lengths of the box along each axis.
// @param transformation The matrix which aligns and scales the box, and its translation vector represents the center of the box.
OrientedBoundingBox(const Vector3& extents, const Matrix& transformation)
OrientedBoundingBox(const Vector3& extents, const Transform& transformation)
{
Extents = extents;
Transformation = transformation;
}
OrientedBoundingBox(const BoundingBox& bb);
OrientedBoundingBox(const Vector3& extents, const Matrix& transformation);
OrientedBoundingBox(const Vector3& extents, const Matrix3x3& rotationScale, const Vector3& translation);
// Init
// @param minimum The minimum vertex of the bounding box.
// @param maximum The maximum vertex of the bounding box.
OrientedBoundingBox(const Vector3& minimum, const Vector3& maximum)
{
const Vector3 center = minimum + (maximum - minimum) / 2.0f;
Extents = maximum - center;
Matrix::Translation(center, Transformation);
}
// Init
// @param points The points that will be contained by the box.
// @param pointCount Amount of the points in th array.
OrientedBoundingBox(const Vector3& minimum, const Vector3& maximum);
OrientedBoundingBox(Vector3 points[], int32 pointCount);
public:
@@ -77,9 +66,9 @@ public:
Vector3 GetSizeSquared() const;
// Gets the center of the OBB.
Vector3 GetCenter() const
FORCE_INLINE Vector3 GetCenter() const
{
return Transformation.GetTranslation();
return Transformation.Translation;
}
/// <summary>
@@ -98,6 +87,7 @@ public:
// Transforms this box using a transformation matrix.
// @param mat The transformation matrix.
void Transform(const Matrix& matrix);
void Transform(const ::Transform& transform);
// Scales the OBB by scaling its Extents without affecting the Transformation matrix.
// By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
@@ -119,7 +109,7 @@ public:
// @param translation the translation vector.
void Translate(const Vector3& translation)
{
Transformation.SetTranslation(Transformation.GetTranslation() + translation);
Transformation.Translation += translation;
}
public:
@@ -140,12 +130,11 @@ public:
return result;
}
private:
static void GetRows(const Matrix& mat, Vector3 rows[3])
FORCE_INLINE OrientedBoundingBox operator*(const ::Transform& matrix) const
{
rows[0] = Vector3(mat.M11, mat.M12, mat.M13);
rows[1] = Vector3(mat.M21, mat.M22, mat.M23);
rows[2] = Vector3(mat.M31, mat.M32, mat.M33);
OrientedBoundingBox result = *this;
result.Transform(matrix);
return result;
}
public:
@@ -157,8 +146,8 @@ public:
/// <param name="result">The result.</param>
static void CreateCentered(const Vector3& center, const Vector3& size, OrientedBoundingBox& result)
{
result.Extents = size / 2.0f;
Matrix::Translation(center, result.Transformation);
result.Extents = size * 0.5f;
result.Transformation = ::Transform(center);
}
/// <summary>
@@ -170,8 +159,7 @@ public:
static OrientedBoundingBox CreateCentered(const Vector3& center, const Vector3& size)
{
OrientedBoundingBox result;
result.Extents = size / 2.0f;
Matrix::Translation(center, result.Transformation);
CreateCentered(center, size, result);
return result;
}
@@ -181,12 +169,6 @@ public:
// @returns The type of containment the two objects have.
ContainmentType Contains(const Vector3& point, Real* distance = nullptr) const;
// Determines whether a OBB contains an array of points.
// @param pointsCnt Amount of points to test.
// @param points The points array to test.
// @returns The type of containment.
ContainmentType Contains(int32 pointsCnt, Vector3 points[]) const;
// Determines whether a OBB contains a BoundingSphere.
// @param sphere The sphere to test.
// @param ignoreScale Optimize the check operation by assuming that OBB has no scaling applied.

View File

@@ -2,11 +2,17 @@
#include "Transform.h"
#include "Matrix.h"
#include "Vector2.h"
#include "Matrix3x3.h"
#include "../Types/String.h"
Transform Transform::Identity(Vector3(0, 0, 0));
Transform::Transform(const Vector3& position, const Matrix3x3& rotationScale)
: Translation(position)
{
rotationScale.Decompose(Scale, Orientation);
}
String Transform::ToString() const
{
return String::Format(TEXT("{}"), *this);
@@ -122,11 +128,10 @@ void Transform::LocalToWorld(const Transform& other, Transform& result) const
result.Translation = Vector3(tmp.X + Translation.X, tmp.Y + Translation.Y, tmp.Z + Translation.Z);
}
Vector3 Transform::LocalToWorldVector(const Vector3& vector) const
void Transform::LocalToWorldVector(const Vector3& vector, Vector3& result) const
{
Vector3 result = vector * Scale;
Vector3::Transform(result, Orientation, result);
return result;
Vector3 tmp = vector * Scale;
Vector3::Transform(tmp, Orientation, result);
}
void Transform::LocalToWorld(const Vector3& point, Vector3& result) const
@@ -145,9 +150,7 @@ void Transform::WorldToLocal(const Transform& other, Transform& result) const
invScale.Y = 1.0f / invScale.Y;
if (invScale.Z != 0.0f)
invScale.Z = 1.0f / invScale.Z;
const Quaternion invRotation = Orientation.Conjugated();
Quaternion::Multiply(invRotation, other.Orientation, result.Orientation);
result.Orientation.Normalize();
Float3::Multiply(other.Scale, invScale, result.Scale);
@@ -171,7 +174,7 @@ void Transform::WorldToLocal(const Vector3& point, Vector3& result) const
result *= invScale;
}
Vector3 Transform::WorldToLocalVector(const Vector3& vector) const
void Transform::WorldToLocalVector(const Vector3& vector, Vector3& result) const
{
Float3 invScale = Scale;
if (invScale.X != 0.0f)
@@ -180,13 +183,9 @@ Vector3 Transform::WorldToLocalVector(const Vector3& vector) const
invScale.Y = 1.0f / invScale.Y;
if (invScale.Z != 0.0f)
invScale.Z = 1.0f / invScale.Z;
const Quaternion invRotation = Orientation.Conjugated();
Vector3 result;
Vector3::Transform(vector, invRotation, result);
return result * invScale;
result *= invScale;
}
Transform Transform::Lerp(const Transform& t1, const Transform& t2, float amount)

View File

@@ -249,6 +249,41 @@ namespace FlaxEngine
return vector;
}
/// <summary>
/// Perform transformation of the given transform in local space
/// </summary>
/// <param name="other">Local space transform</param>
/// <param name="result">World space transform</param>
public void LocalToWorld(ref Transform other, out Transform result)
{
Quaternion.Multiply(ref Orientation, ref other.Orientation, out result.Orientation);
Float3.Multiply(ref Scale, ref other.Scale, out result.Scale);
result.Translation = LocalToWorld(other.Translation);
}
/// <summary>
/// Perform transformation of the given point in local space
/// </summary>
/// <param name="point">Local space point</param>
/// <param name="result">World space point</param>
public void LocalToWorld(ref Vector3 point, out Vector3 result)
{
Vector3 tmp = point * Scale;
Vector3.Transform(ref tmp, ref Orientation, out result);
result += Translation;
}
/// <summary>
/// Performs transformation of the given vector in local space to the world space of this transform.
/// </summary>
/// <param name="vector">The local space vector.</param>
/// <param name="result">World space vector</param>
public void LocalToWorldVector(ref Vector3 vector, out Vector3 result)
{
Vector3 tmp = vector * Scale;
Vector3.Transform(ref tmp, ref Orientation, out result);
}
/// <summary>
/// Perform transformation of the given points in local space
/// </summary>
@@ -276,14 +311,12 @@ namespace FlaxEngine
invScale.Y = 1.0f / invScale.Y;
if (invScale.Z != 0.0f)
invScale.Z = 1.0f / invScale.Z;
Transform result;
result.Orientation = Orientation;
result.Orientation.Invert();
Quaternion.Multiply(ref result.Orientation, ref other.Orientation, out result.Orientation);
Float3.Multiply(ref other.Scale, ref invScale, out result.Scale);
result.Translation = WorldToLocal(other.Translation);
return result;
}
@@ -301,13 +334,9 @@ namespace FlaxEngine
invScale.Y = 1.0f / invScale.Y;
if (invScale.Z != 0.0f)
invScale.Z = 1.0f / invScale.Z;
Quaternion invRotation = Orientation;
invRotation.Invert();
Quaternion invRotation = Orientation.Conjugated();
Vector3 result = point - Translation;
Vector3.Transform(ref result, ref invRotation, out result);
return result * invScale;
}
@@ -325,15 +354,51 @@ namespace FlaxEngine
invScale.Y = 1.0f / invScale.Y;
if (invScale.Z != 0.0f)
invScale.Z = 1.0f / invScale.Z;
Quaternion invRotation = Orientation;
invRotation.Invert();
Quaternion invRotation = Orientation.Conjugated();
Vector3.Transform(ref vector, ref invRotation, out var result);
return result * invScale;
}
/// <summary>
/// Perform transformation of the given point in world space
/// </summary>
/// <param name="point">World space point</param>
/// <param name="result">When the method completes, contains the local space point.</param>
/// <returns>Local space point</returns>
public void WorldToLocal(ref Vector3 point, out Vector3 result)
{
var invScale = Scale;
if (invScale.X != 0.0f)
invScale.X = 1.0f / invScale.X;
if (invScale.Y != 0.0f)
invScale.Y = 1.0f / invScale.Y;
if (invScale.Z != 0.0f)
invScale.Z = 1.0f / invScale.Z;
Quaternion invRotation = Orientation.Conjugated();
Vector3 tmp = point - Translation;
Vector3.Transform(ref tmp, ref invRotation, out result);
result *= invScale;
}
/// <summary>
/// Perform transformation of the given vector in world space
/// </summary>
/// <param name="vector">World space vector</param>
/// <param name="result">Local space vector</param>
public void WorldToLocalVector(ref Vector3 vector, out Vector3 result)
{
var invScale = Scale;
if (invScale.X != 0.0f)
invScale.X = 1.0f / invScale.X;
if (invScale.Y != 0.0f)
invScale.Y = 1.0f / invScale.Y;
if (invScale.Z != 0.0f)
invScale.Z = 1.0f / invScale.Z;
Quaternion invRotation = Orientation.Conjugated();
Vector3.Transform(ref vector, ref invRotation, out result);
result *= invScale;
}
/// <summary>
/// Perform transformation of the given points in world space
/// </summary>
@@ -348,10 +413,7 @@ namespace FlaxEngine
invScale.Y = 1.0f / invScale.Y;
if (invScale.Z != 0.0f)
invScale.Z = 1.0f / invScale.Z;
Quaternion invRotation = Orientation;
invRotation.Invert();
Quaternion invRotation = Orientation.Conjugated();
for (int i = 0; i < points.Length; i++)
{
result[i] = points[i] - Translation;

View File

@@ -80,6 +80,8 @@ public:
{
}
Transform(const Vector3& position, const Matrix3x3& rotationScale);
public:
String ToString() const;
@@ -192,12 +194,24 @@ public:
return result;
}
/// <summary>
/// Performs transformation of the given vector in local space to the world space of this transform.
/// </summary>
/// <param name="vector">The local space vector.</param>
/// <param name="result">The world space vector.</param>
void LocalToWorldVector(const Vector3& vector, Vector3& result) const;
/// <summary>
/// Performs transformation of the given vector in local space to the world space of this transform.
/// </summary>
/// <param name="vector">The local space vector.</param>
/// <returns>The world space vector.</returns>
Vector3 LocalToWorldVector(const Vector3& vector) const;
Vector3 LocalToWorldVector(const Vector3& vector) const
{
Vector3 result;
LocalToWorldVector(vector, result);
return result;
}
/// <summary>
/// Performs transformation of the given point in local space to the world space of this transform.
@@ -244,12 +258,24 @@ public:
return result;
}
/// <summary>
/// Performs transformation of the given vector in world space to the local space of this transform.
/// </summary>
/// <param name="vector">The world space vector.</param>
/// <param name="result">The local space vector.</param>
void WorldToLocalVector(const Vector3& vector, Vector3& result) const;
/// <summary>
/// Performs transformation of the given vector in world space to the local space of this transform.
/// </summary>
/// <param name="vector">The world space vector.</param>
/// <returns>The local space vector.</returns>
Vector3 WorldToLocalVector(const Vector3& vector) const;
Vector3 WorldToLocalVector(const Vector3& vector) const
{
Vector3 result;
WorldToLocalVector(vector, result);
return result;
}
public:
FORCE_INLINE Transform operator*(const Transform& other) const

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "BoxBrush.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Content/Content.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Level/Scene/Scene.h"

View File

@@ -190,7 +190,7 @@ private:
FORCE_INLINE void UpdateBounds()
{
OrientedBoundingBox::CreateCentered(_center, _size, _bounds);
_bounds.Transform(_transform.GetWorld());
_bounds.Transform(_transform);
_bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "BoxVolume.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Serialization/Serialization.h"
BoxVolume::BoxVolume(const SpawnParams& params)
@@ -54,11 +55,13 @@ namespace
else
Quaternion::LookRotation(dir, Vector3::Cross(Vector3::Cross(dir, Vector3::Up), dir), orientation);
const Vector3 up = orientation * Vector3::Up;
Matrix::CreateWorld(min + vec * 0.5f, dir, up, box.Transformation);
Matrix inv;
Matrix::Invert(box.Transformation, inv);
Matrix world;
Matrix::CreateWorld(min + vec * 0.5f, dir, up, world);
world.Decompose(box.Transformation);
Matrix invWorld;
Matrix::Invert(world, invWorld);
Vector3 vecLocal;
Vector3::TransformNormal(vec * 0.5f, inv, vecLocal);
Vector3::TransformNormal(vec * 0.5f, invWorld, vecLocal);
box.Extents.X = margin;
box.Extents.Y = margin;
box.Extents.Z = vecLocal.Z;

View File

@@ -15,7 +15,7 @@ Decal::Decal(const SpawnParams& params)
{
_world = Matrix::Scaling(_size);
_bounds.Extents = Vector3::Half;
_bounds.Transformation = _world;
_world.Decompose(_bounds.Transformation);
_bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);
}
@@ -30,8 +30,8 @@ void Decal::SetSize(const Vector3& value)
Transform t = _transform;
t.Scale *= _size;
t.GetWorld(_world);
_bounds.Extents = Vector3::Half;
_bounds.Transformation = _world;
_bounds.Transformation = t;
_bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);
}
@@ -152,7 +152,7 @@ void Decal::OnTransformChanged()
t.GetWorld(_world);
_bounds.Extents = Vector3::Half;
_bounds.Transformation = _world;
_bounds.Transformation = t;
_bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);

View File

@@ -3,6 +3,7 @@
#pragma once
#include "../Actor.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Math/OrientedBoundingBox.h"
#include "Engine/Content/Assets/MaterialBase.h"
#include "Engine/Content/AssetReference.h"

View File

@@ -60,11 +60,13 @@ namespace
else
Quaternion::LookRotation(dir, Float3::Cross(Float3::Cross(dir, Float3::Up), dir), orientation);
const Vector3 up = orientation * Vector3::Up;
Matrix::CreateWorld(min + vec * 0.5f, dir, up, box.Transformation);
Matrix inv;
Matrix::Invert(box.Transformation, inv);
Matrix world;
Matrix::CreateWorld(min + vec * 0.5f, dir, up, world);
world.Decompose(box.Transformation);
Matrix invWorld;
Matrix::Invert(world, invWorld);
Vector3 vecLocal;
Vector3::TransformNormal(vec * 0.5f, inv, vecLocal);
Vector3::TransformNormal(vec * 0.5f, invWorld, vecLocal);
box.Extents.X = margin;
box.Extents.Y = margin;
box.Extents.Z = vecLocal.Z;

View File

@@ -0,0 +1,72 @@
// Copyright (c) 2014-2022 Wojciech Figat. All rights reserved.
using System;
namespace FlaxEngine
{
partial class CollisionData
{
/// <summary>
/// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread.
/// [Deprecated on 16.06.2022, expires on 16.06.2024]
/// </summary>
/// <remarks>
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
/// </remarks>
/// <param name="type">The collision data type.</param>
/// <param name="vertices">The source geometry vertex buffer with vertices positions. Cannot be empty.</param>
/// <param name="triangles">The source data index buffer (triangles list). Uses 32-bit stride buffer. Cannot be empty. Length must be multiple of 3 (as 3 vertices build a triangle).</param>
/// <param name="convexFlags">The convex mesh generation flags.</param>
/// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param>
/// <returns>True if failed, otherwise false.</returns>
[Obsolete("Deprecated in 1.4")]
public bool CookCollision(CollisionDataType type, Vector3[] vertices, uint[] triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255)
{
if (vertices == null)
throw new ArgumentNullException();
var tmp = new Float3[vertices.Length];
for (int i = 0; i < tmp.Length; i++)
tmp[i] = vertices[i];
return CookCollision(type, tmp, triangles, convexFlags, convexVertexLimit);
}
/// <summary>
/// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread.
/// [Deprecated on 16.06.2022, expires on 16.06.2024]
/// </summary>
/// <remarks>
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
/// </remarks>
/// <param name="type">The collision data type.</param>
/// <param name="vertices">The source geometry vertex buffer with vertices positions. Cannot be empty.</param>
/// <param name="triangles">The source data index buffer (triangles list). Uses 32-bit stride buffer. Cannot be empty. Length must be multiple of 3 (as 3 vertices build a triangle).</param>
/// <param name="convexFlags">The convex mesh generation flags.</param>
/// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param>
/// <returns>True if failed, otherwise false.</returns>
[Obsolete("Deprecated in 1.4")]
public bool CookCollision(CollisionDataType type, Vector3[] vertices, int[] triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255)
{
if (vertices == null)
throw new ArgumentNullException();
var tmp = new Float3[vertices.Length];
for (int i = 0; i < tmp.Length; i++)
tmp[i] = vertices[i];
return CookCollision(type, tmp, triangles, convexFlags, convexVertexLimit);
}
/// <summary>
/// Extracts the collision data geometry into list of triangles.
/// [Deprecated on 16.06.2022, expires on 16.06.2024]
/// </summary>
/// <param name="vertexBuffer">The output vertex buffer.</param>
/// <param name="indexBuffer">The output index buffer.</param>
[Obsolete("Deprecated in 1.4")]
public void ExtractGeometry(out Vector3[] vertexBuffer, out int[] indexBuffer)
{
ExtractGeometry(out Float3[] tmp, out indexBuffer);
vertexBuffer = new Vector3[tmp.Length];
for (int i = 0; i < tmp.Length; i++)
vertexBuffer[i] = tmp[i];
}
}
}

View File

@@ -1074,8 +1074,10 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con
}
// Write to objects buffer (this must match unpacking logic in HLSL)
Matrix localToWorldBounds;
object->Bounds.Transformation.GetWorld(localToWorldBounds);
Matrix worldToLocalBounds;
Matrix::Invert(object->Bounds.Transformation, worldToLocalBounds);
Matrix::Invert(localToWorldBounds, worldToLocalBounds);
uint32 objectAddress = _objectsBuffer->Data.Count() / sizeof(Float4);
auto* objectData = _objectsBuffer->WriteReserve<Float4>(GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE);
objectData[0] = *(Float4*)&actorObjectBounds;
@@ -1103,13 +1105,13 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con
yAxis = tileIndex == 2 || tileIndex == 3 ? Float3::Right : Float3::Up;
Float3::Cross(yAxis, zAxis, xAxis);
Float3 localSpaceOffset = -zAxis * object->Bounds.Extents;
Float3::TransformNormal(xAxis, object->Bounds.Transformation, xAxis);
Float3::TransformNormal(yAxis, object->Bounds.Transformation, yAxis);
Float3::TransformNormal(zAxis, object->Bounds.Transformation, zAxis);
xAxis = object->Bounds.Transformation.LocalToWorldVector(xAxis);
yAxis = object->Bounds.Transformation.LocalToWorldVector(yAxis);
zAxis = object->Bounds.Transformation.LocalToWorldVector(zAxis);
xAxis.NormalizeFast();
yAxis.NormalizeFast();
zAxis.NormalizeFast();
Float3::Transform(localSpaceOffset, object->Bounds.Transformation, tile->ViewPosition);
object->Bounds.Transformation.LocalToWorld(localSpaceOffset, tile->ViewPosition);
tile->ViewDirection = zAxis;
// Create view matrix
@@ -1122,7 +1124,7 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con
OrientedBoundingBox viewBounds(object->Bounds);
viewBounds.Transform(tile->ViewMatrix);
Float3 viewExtent;
Float3::TransformNormal(viewBounds.Extents, viewBounds.Transformation, viewExtent);
viewExtent = viewBounds.Transformation.LocalToWorldVector(viewBounds.Extents);
tile->ViewBoundsSize = viewExtent.GetAbsolute() * 2.0f;
// Per-tile data

View File

@@ -7,8 +7,6 @@
#include "Engine/Utilities/StringConverter.h"
struct CommonValue;
struct Matrix;
struct Transform;
class ISerializable;
// Helper macro for JSON serialization keys (reduces allocations count)

View File

@@ -39,7 +39,8 @@ namespace FlaxEngine.GUI
child.Canvas.GetWorldMatrix(out var world);
Matrix.Translation((float)bounds.Extents.X, (float)bounds.Extents.Y, 0, out var offset);
Matrix.Multiply(ref offset, ref world, out bounds.Transformation);
Matrix.Multiply(ref offset, ref world, out var boxWorld);
boxWorld.Decompose(out bounds.Transformation);
// Hit test
if (bounds.Intersects(ref ray, out Vector3 hitPoint))

View File

@@ -330,7 +330,8 @@ namespace FlaxEngine
};
GetWorldMatrix(out Matrix world);
Matrix.Translation((float)bounds.Extents.X, (float)bounds.Extents.Y, 0, out Matrix offset);
Matrix.Multiply(ref offset, ref world, out bounds.Transformation);
Matrix.Multiply(ref offset, ref world, out var boxWorld);
boxWorld.Decompose(out bounds.Transformation);
return bounds;
}
}

View File

@@ -122,7 +122,8 @@ namespace FlaxEngine
canvasRoot.Canvas.GetWorldMatrix(out Matrix world);
Matrix.Translation(min.X + size.X * 0.5f, min.Y + size.Y * 0.5f, 0, out Matrix offset);
Matrix.Multiply(ref offset, ref world, out bounds.Transformation);
Matrix.Multiply(ref offset, ref world, out var boxWorld);
boxWorld.Decompose(out bounds.Transformation);
return bounds;
}
}

View File

@@ -878,11 +878,15 @@ namespace Flax.Build.Bindings
contents.Append("protected ");
else if (fieldInfo.Access == AccessLevel.Private)
contents.Append("private ");
if (fieldInfo.IsStatic)
if (fieldInfo.IsConstexpr)
contents.Append("const ");
else if (fieldInfo.IsStatic)
contents.Append("static ");
var returnValueType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, classInfo);
contents.Append(returnValueType).Append(' ').Append(fieldInfo.Name);
if (!useUnmanaged)
if (!useUnmanaged || fieldInfo.IsConstexpr)
{
var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, classInfo, fieldInfo.Type);
if (!string.IsNullOrEmpty(defaultValue))
@@ -1156,7 +1160,9 @@ namespace Flax.Build.Bindings
contents.Append("protected ");
else if (fieldInfo.Access == AccessLevel.Private)
contents.Append("private ");
if (fieldInfo.IsStatic)
if (fieldInfo.IsConstexpr)
contents.Append("const ");
else if (fieldInfo.IsStatic)
contents.Append("static ");
string type;
@@ -1189,9 +1195,17 @@ namespace Flax.Build.Bindings
type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
contents.Append(type).Append(' ').Append(fieldInfo.Name);
// Static fields are using C++ static value accessed via getter function binding
if (fieldInfo.IsStatic)
if (fieldInfo.IsConstexpr)
{
// Compile-time constant
var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, structureInfo, fieldInfo.Type);
if (!string.IsNullOrEmpty(defaultValue))
contents.Append(" = ").Append(defaultValue);
contents.AppendLine(";");
}
else if (fieldInfo.IsStatic)
{
// Static fields are using C++ static value accessed via getter function binding
contents.AppendLine();
contents.AppendLine(indent + "{");
indent += " ";
@@ -1386,7 +1400,7 @@ namespace Flax.Build.Bindings
else if (type is InjectCodeInfo injectCodeInfo && string.Equals(injectCodeInfo.Lang, "csharp", StringComparison.OrdinalIgnoreCase))
{
// `using` directives needs to go above the generated code
foreach(var code in injectCodeInfo.Code.Split(';'))
foreach (var code in injectCodeInfo.Code.Split(';'))
{
if (code.StartsWith("using"))
CSharpUsedNamespaces.Add(code.Substring(6));

View File

@@ -19,7 +19,7 @@ namespace Flax.Build.Bindings
partial class BindingsGenerator
{
private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
private const int CacheVersion = 14;
private const int CacheVersion = 15;
internal static void Write(BinaryWriter writer, string e)
{

View File

@@ -1384,7 +1384,7 @@ namespace Flax.Build.Bindings
private static bool GenerateCppAutoSerializationSkip(BuildData buildData, ApiTypeInfo caller, MemberInfo memberInfo, TypeInfo typeInfo)
{
if (memberInfo.IsStatic)
if (memberInfo.IsStatic || memberInfo.IsConstexpr)
return true;
if (memberInfo.Access != AccessLevel.Public && !memberInfo.HasAttribute("Serialize"))
return true;
@@ -1691,7 +1691,7 @@ namespace Flax.Build.Bindings
// Fields
foreach (var fieldInfo in classInfo.Fields)
{
if (!useScripting || !useCSharp || fieldInfo.IsHidden)
if (!useScripting || !useCSharp || fieldInfo.IsHidden || fieldInfo.IsConstexpr)
continue;
if (fieldInfo.Getter != null)
GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Getter, "{0}");
@@ -1853,6 +1853,9 @@ namespace Flax.Build.Bindings
// Fields
foreach (var fieldInfo in structureInfo.Fields)
{
if (fieldInfo.IsConstexpr)
continue;
// Static fields are using C++ static value accessed via getter function binding
if (fieldInfo.IsStatic)
{
@@ -1977,7 +1980,7 @@ namespace Flax.Build.Bindings
for (var i = 0; i < structureInfo.Fields.Count; i++)
{
var fieldInfo = structureInfo.Fields[i];
if (fieldInfo.IsReadOnly || fieldInfo.IsStatic || fieldInfo.Access == AccessLevel.Private)
if (fieldInfo.IsReadOnly || fieldInfo.IsStatic || fieldInfo.IsConstexpr || fieldInfo.Access == AccessLevel.Private)
continue;
if (i == 0)
contents.AppendLine($" if (name == TEXT(\"{fieldInfo.Name}\"))");
@@ -1993,7 +1996,7 @@ namespace Flax.Build.Bindings
for (var i = 0; i < structureInfo.Fields.Count; i++)
{
var fieldInfo = structureInfo.Fields[i];
if (fieldInfo.IsReadOnly || fieldInfo.IsStatic || fieldInfo.Access == AccessLevel.Private)
if (fieldInfo.IsReadOnly || fieldInfo.IsStatic || fieldInfo.IsConstexpr || fieldInfo.Access == AccessLevel.Private)
continue;
if (i == 0)
contents.AppendLine($" if (name == TEXT(\"{fieldInfo.Name}\"))");
@@ -2205,7 +2208,7 @@ namespace Flax.Build.Bindings
for (var i = 0; i < structureInfo.Fields.Count; i++)
{
var fieldInfo = structureInfo.Fields[i];
if (fieldInfo.IsStatic)
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
var fieldType = fieldInfo.Type;
var fieldApiType = FindApiTypeInfo(buildData, fieldType, structureInfo);
@@ -2450,7 +2453,7 @@ namespace Flax.Build.Bindings
for (var i = 0; i < fields.Count; i++)
{
var fieldInfo = fields[i];
if (fieldInfo.IsStatic)
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
if (fieldInfo.NoArray && fieldInfo.Type.IsArray)
@@ -2482,7 +2485,7 @@ namespace Flax.Build.Bindings
for (var i = 0; i < fields.Count; i++)
{
var fieldInfo = fields[i];
if (fieldInfo.IsStatic)
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
CppNonPodTypesConvertingGeneration = true;
@@ -2531,7 +2534,7 @@ namespace Flax.Build.Bindings
for (var i = 0; i < fields.Count; i++)
{
var fieldInfo = fields[i];
if (fieldInfo.IsStatic)
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
if (fieldInfo.NoArray && fieldInfo.Type.IsArray)
@@ -2566,7 +2569,7 @@ namespace Flax.Build.Bindings
for (var i = 0; i < fields.Count; i++)
{
var fieldInfo = fields[i];
if (fieldInfo.IsStatic)
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
CppNonPodTypesConvertingGeneration = true;

View File

@@ -1155,6 +1155,11 @@ namespace Flax.Build.Bindings
desc.IsStatic = true;
context.Tokenizer.NextToken();
}
else if (!desc.IsConstexpr && token.Value == "constexpr")
{
desc.IsConstexpr = true;
context.Tokenizer.NextToken();
}
else if (!isMutable && token.Value == "mutable")
{
isMutable = true;

View File

@@ -12,6 +12,7 @@ namespace Flax.Build.Bindings
public string Name;
public string[] Comment;
public bool IsStatic;
public bool IsConstexpr;
public bool IsDeprecated;
public bool IsHidden;
public AccessLevel Access;
@@ -27,6 +28,7 @@ namespace Flax.Build.Bindings
writer.Write(Name);
BindingsGenerator.Write(writer, Comment);
writer.Write(IsStatic);
writer.Write(IsConstexpr);
writer.Write(IsDeprecated);
writer.Write(IsHidden);
writer.Write((byte)Access);
@@ -38,6 +40,7 @@ namespace Flax.Build.Bindings
Name = reader.ReadString();
Comment = BindingsGenerator.Read(reader, Comment);
IsStatic = reader.ReadBoolean();
IsConstexpr = reader.ReadBoolean();
IsDeprecated = reader.ReadBoolean();
IsHidden = reader.ReadBoolean();
Access = (AccessLevel)reader.ReadByte();