diff --git a/Source/Engine/Core/Math/Double4x4.h b/Source/Engine/Core/Math/Double4x4.h
new file mode 100644
index 000000000..0f7f5cb7d
--- /dev/null
+++ b/Source/Engine/Core/Math/Double4x4.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Vector4.h"
+
+///
+/// Represents a 4x4 mathematical matrix using double-precision floating-point values.
+///
+struct FLAXENGINE_API Double4x4
+{
+public:
+ union
+ {
+ struct
+ {
+ /// Value at row 1 column 1 of the matrix.
+ double M11;
+
+ /// Value at row 1 column 2 of the matrix.
+ double M12;
+
+ /// Value at row 1 column 3 of the matrix.
+ double M13;
+
+ /// Value at row 1 column 4 of the matrix.
+ double M14;
+
+ /// Value at row 2 column 1 of the matrix.
+ double M21;
+
+ /// Value at row 2 column 2 of the matrix.
+ double M22;
+
+ /// Value at row 2 column 3 of the matrix.
+ double M23;
+
+ /// Value at row 2 column 4 of the matrix.
+ double M24;
+
+ /// Value at row 3 column 1 of the matrix.
+ double M31;
+
+ /// Value at row 3 column 2 of the matrix.
+ double M32;
+
+ /// Value at row 3 column 3 of the matrix.
+ double M33;
+
+ /// Value at row 3 column 4 of the matrix.
+ double M34;
+
+ /// Value at row 4 column 1 of the matrix.
+ double M41;
+
+ /// Value at row 4 column 2 of the matrix.
+ double M42;
+
+ /// Value at row 4 column 3 of the matrix.
+ double M43;
+
+ /// Value at row 4 column 4 of the matrix.
+ double M44;
+ };
+
+ double Values[4][4];
+ double Raw[16];
+ };
+
+public:
+ ///
+ /// Empty constructor.
+ ///
+ Double4x4() = default;
+
+ Double4x4(const Matrix& matrix);
+
+public:
+ // Inverts the matrix.
+ void Invert()
+ {
+ Invert(*this, *this);
+ }
+
+ // Calculates the inverse of the specified matrix.
+ static Double4x4 Invert(const Double4x4& value)
+ {
+ Double4x4 result;
+ Invert(value, result);
+ return result;
+ }
+
+ // Calculates the inverse of the specified matrix.
+ static void Invert(const Double4x4& value, Double4x4& result);
+
+ // Calculates the product of two matrices.
+ static void Multiply(const Double4x4& left, const Double4x4& right, Double4x4& result);
+
+ // Creates a matrix that contains both the X, Y and Z rotation, as well as scaling and translation.
+ static void Transformation(const Float3& scaling, const Quaternion& rotation, const Vector3& translation, Double4x4& result);
+
+public:
+ Double4x4 operator*(const Double4x4& other) const
+ {
+ Double4x4 result;
+ Multiply(*this, other, result);
+ return result;
+ }
+};
+
+template<>
+struct TIsPODType
+{
+ enum { Value = true };
+};
diff --git a/Source/Engine/Core/Math/Matrix.cpp b/Source/Engine/Core/Math/Matrix.cpp
index 872b554a6..38950d965 100644
--- a/Source/Engine/Core/Math/Matrix.cpp
+++ b/Source/Engine/Core/Math/Matrix.cpp
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "Matrix.h"
+#include "Double4x4.h"
#include "Matrix3x3.h"
#include "Matrix3x4.h"
#include "Vector2.h"
@@ -28,6 +29,12 @@ Matrix::Matrix(const Matrix3x3& matrix)
M44 = 1.0f;
}
+Matrix::Matrix(const Double4x4& matrix)
+{
+ for (int32 i = 0; i < 16; i++)
+ Raw[i] = (float)matrix.Raw[i];
+}
+
String Matrix::ToString() const
{
return String::Format(TEXT("{}"), *this);
@@ -924,3 +931,144 @@ void Matrix3x4::SetMatrixTranspose(const Matrix& m)
dst[10] = src[10];
dst[11] = src[14];
}
+
+Double4x4::Double4x4(const Matrix& matrix)
+{
+ for (int32 i = 0; i < 16; i++)
+ Raw[i] = matrix.Raw[i];
+}
+
+void Double4x4::Invert(const Double4x4& value, Double4x4& result)
+{
+ const double b0 = value.M31 * value.M42 - value.M32 * value.M41;
+ const double b1 = value.M31 * value.M43 - value.M33 * value.M41;
+ const double b2 = value.M34 * value.M41 - value.M31 * value.M44;
+ const double b3 = value.M32 * value.M43 - value.M33 * value.M42;
+ const double b4 = value.M34 * value.M42 - value.M32 * value.M44;
+ const double b5 = value.M33 * value.M44 - value.M34 * value.M43;
+
+ const double d11 = value.M22 * b5 + value.M23 * b4 + value.M24 * b3;
+ const double d12 = value.M21 * b5 + value.M23 * b2 + value.M24 * b1;
+ const double d13 = value.M21 * -b4 + value.M22 * b2 + value.M24 * b0;
+ const double d14 = value.M21 * b3 + value.M22 * -b1 + value.M23 * b0;
+
+ double det = value.M11 * d11 - value.M12 * d12 + value.M13 * d13 - value.M14 * d14;
+ if (Math::Abs(det) <= 1e-12)
+ {
+ Platform::MemoryClear(&result, sizeof(Double4x4));
+ return;
+ }
+
+ det = 1.0 / det;
+
+ const double a0 = value.M11 * value.M22 - value.M12 * value.M21;
+ const double a1 = value.M11 * value.M23 - value.M13 * value.M21;
+ const double a2 = value.M14 * value.M21 - value.M11 * value.M24;
+ const double a3 = value.M12 * value.M23 - value.M13 * value.M22;
+ const double a4 = value.M14 * value.M22 - value.M12 * value.M24;
+ const double a5 = value.M13 * value.M24 - value.M14 * value.M23;
+
+ const double d21 = value.M12 * b5 + value.M13 * b4 + value.M14 * b3;
+ const double d22 = value.M11 * b5 + value.M13 * b2 + value.M14 * b1;
+ const double d23 = value.M11 * -b4 + value.M12 * b2 + value.M14 * b0;
+ const double d24 = value.M11 * b3 + value.M12 * -b1 + value.M13 * b0;
+
+ const double d31 = value.M42 * a5 + value.M43 * a4 + value.M44 * a3;
+ const double d32 = value.M41 * a5 + value.M43 * a2 + value.M44 * a1;
+ const double d33 = value.M41 * -a4 + value.M42 * a2 + value.M44 * a0;
+ const double d34 = value.M41 * a3 + value.M42 * -a1 + value.M43 * a0;
+
+ const double d41 = value.M32 * a5 + value.M33 * a4 + value.M34 * a3;
+ const double d42 = value.M31 * a5 + value.M33 * a2 + value.M34 * a1;
+ const double d43 = value.M31 * -a4 + value.M32 * a2 + value.M34 * a0;
+ const double d44 = value.M31 * a3 + value.M32 * -a1 + value.M33 * a0;
+
+ result.M11 = +d11 * det;
+ result.M12 = -d21 * det;
+ result.M13 = +d31 * det;
+ result.M14 = -d41 * det;
+ result.M21 = -d12 * det;
+ result.M22 = +d22 * det;
+ result.M23 = -d32 * det;
+ result.M24 = +d42 * det;
+ result.M31 = +d13 * det;
+ result.M32 = -d23 * det;
+ result.M33 = +d33 * det;
+ result.M34 = -d43 * det;
+ result.M41 = -d14 * det;
+ result.M42 = +d24 * det;
+ result.M43 = -d34 * det;
+ result.M44 = +d44 * det;
+}
+
+void Double4x4::Multiply(const Double4x4& left, const Double4x4& right, Double4x4& result)
+{
+ result.M11 = left.M11 * right.M11 + left.M12 * right.M21 + left.M13 * right.M31 + left.M14 * right.M41;
+ result.M12 = left.M11 * right.M12 + left.M12 * right.M22 + left.M13 * right.M32 + left.M14 * right.M42;
+ result.M13 = left.M11 * right.M13 + left.M12 * right.M23 + left.M13 * right.M33 + left.M14 * right.M43;
+ result.M14 = left.M11 * right.M14 + left.M12 * right.M24 + left.M13 * right.M34 + left.M14 * right.M44;
+ result.M21 = left.M21 * right.M11 + left.M22 * right.M21 + left.M23 * right.M31 + left.M24 * right.M41;
+ result.M22 = left.M21 * right.M12 + left.M22 * right.M22 + left.M23 * right.M32 + left.M24 * right.M42;
+ result.M23 = left.M21 * right.M13 + left.M22 * right.M23 + left.M23 * right.M33 + left.M24 * right.M43;
+ result.M24 = left.M21 * right.M14 + left.M22 * right.M24 + left.M23 * right.M34 + left.M24 * right.M44;
+ result.M31 = left.M31 * right.M11 + left.M32 * right.M21 + left.M33 * right.M31 + left.M34 * right.M41;
+ result.M32 = left.M31 * right.M12 + left.M32 * right.M22 + left.M33 * right.M32 + left.M34 * right.M42;
+ result.M33 = left.M31 * right.M13 + left.M32 * right.M23 + left.M33 * right.M33 + left.M34 * right.M43;
+ result.M34 = left.M31 * right.M14 + left.M32 * right.M24 + left.M33 * right.M34 + left.M34 * right.M44;
+ result.M41 = left.M41 * right.M11 + left.M42 * right.M21 + left.M43 * right.M31 + left.M44 * right.M41;
+ result.M42 = left.M41 * right.M12 + left.M42 * right.M22 + left.M43 * right.M32 + left.M44 * right.M42;
+ result.M43 = left.M41 * right.M13 + left.M42 * right.M23 + left.M43 * right.M33 + left.M44 * right.M43;
+ result.M44 = left.M41 * right.M14 + left.M42 * right.M24 + left.M43 * right.M34 + left.M44 * right.M44;
+}
+
+void Double4x4::Transformation(const Float3& scaling, const Quaternion& rotation, const Vector3& translation, Double4x4& result)
+{
+ // Equivalent to:
+ //result =
+ // Matrix.Scaling(scaling)
+ // *Matrix.RotationX(rotation.X)
+ // *Matrix.RotationY(rotation.Y)
+ // *Matrix.RotationZ(rotation.Z)
+ // *Matrix.Position(translation);
+
+ // Rotation
+ const float xx = rotation.X * rotation.X;
+ const float yy = rotation.Y * rotation.Y;
+ const float zz = rotation.Z * rotation.Z;
+ const float xy = rotation.X * rotation.Y;
+ const float zw = rotation.Z * rotation.W;
+ const float zx = rotation.Z * rotation.X;
+ const float yw = rotation.Y * rotation.W;
+ const float yz = rotation.Y * rotation.Z;
+ const float xw = rotation.X * rotation.W;
+ result.M11 = 1.0f - 2.0f * (yy + zz);
+ result.M12 = 2.0f * (xy + zw);
+ result.M13 = 2.0f * (zx - yw);
+ result.M21 = 2.0f * (xy - zw);
+ result.M22 = 1.0f - 2.0f * (zz + xx);
+ result.M23 = 2.0f * (yz + xw);
+ result.M31 = 2.0f * (zx + yw);
+ result.M32 = 2.0f * (yz - xw);
+ result.M33 = 1.0f - 2.0f * (yy + xx);
+
+ // Position
+ result.M41 = translation.X;
+ result.M42 = translation.Y;
+ result.M43 = translation.Z;
+
+ // Scale
+ result.M11 *= scaling.X;
+ result.M12 *= scaling.X;
+ result.M13 *= scaling.X;
+ result.M21 *= scaling.Y;
+ result.M22 *= scaling.Y;
+ result.M23 *= scaling.Y;
+ result.M31 *= scaling.Z;
+ result.M32 *= scaling.Z;
+ result.M33 *= scaling.Z;
+
+ result.M14 = 0.0;
+ result.M24 = 0.0;
+ result.M34 = 0.0;
+ result.M44 = 1.0;
+}
diff --git a/Source/Engine/Core/Math/Matrix.h b/Source/Engine/Core/Math/Matrix.h
index e71da993a..b4b755da1 100644
--- a/Source/Engine/Core/Math/Matrix.h
+++ b/Source/Engine/Core/Math/Matrix.h
@@ -146,6 +146,7 @@ public:
}
explicit Matrix(const Matrix3x3& matrix);
+ explicit Matrix(const Double4x4& matrix);
public:
String ToString() const;
diff --git a/Source/Engine/Core/Math/Transform.cpp b/Source/Engine/Core/Math/Transform.cpp
index 0106be2f9..3bb8772ee 100644
--- a/Source/Engine/Core/Math/Transform.cpp
+++ b/Source/Engine/Core/Math/Transform.cpp
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "Transform.h"
+#include "Double4x4.h"
#include "Matrix.h"
#include "Matrix3x3.h"
#include "../Types/String.h"
@@ -57,6 +58,11 @@ void Transform::GetWorld(Matrix& result) const
Matrix::Transformation(Scale, Orientation, Translation, result);
}
+void Transform::GetWorld(Double4x4& result) const
+{
+ Double4x4::Transformation(Scale, Orientation, Translation, result);
+}
+
Transform Transform::Add(const Vector3& translation) const
{
Transform result;
diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h
index 8e9e07e0a..9c2f9aea0 100644
--- a/Source/Engine/Core/Math/Transform.h
+++ b/Source/Engine/Core/Math/Transform.h
@@ -136,6 +136,12 @@ public:
/// World matrix
void GetWorld(Matrix& result) const;
+ ///
+ /// Gets world matrix that describes transformation as a 4 by 4 matrix.
+ ///
+ /// World matrix
+ void GetWorld(Double4x4& result) const;
+
public:
///
/// Adds translation to this transform.
diff --git a/Source/Engine/Core/Types/BaseTypes.h b/Source/Engine/Core/Types/BaseTypes.h
index 64db93be9..5bfaf5ce0 100644
--- a/Source/Engine/Core/Types/BaseTypes.h
+++ b/Source/Engine/Core/Types/BaseTypes.h
@@ -126,6 +126,7 @@ API_TYPEDEF(Alias) typedef Vector4Base Vector4;
struct BoundingBox;
struct Matrix;
struct Matrix3x3;
+struct Double4x4;
struct Ray;
struct Plane;
struct Rectangle;
@@ -136,6 +137,11 @@ struct OrientedBoundingBox;
struct Transform;
struct Color;
struct Color32;
+#if USE_LARGE_WORLDS
+typedef Double4x4 Real4x4;
+#else
+typedef Matrix Real4x4;
+#endif
// @formatter:on
diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp
index 730895267..2d71c3e26 100644
--- a/Source/Engine/Graphics/RenderView.cpp
+++ b/Source/Engine/Graphics/RenderView.cpp
@@ -215,6 +215,13 @@ void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const
Matrix::Transformation(transform.Scale, transform.Orientation, translation, world);
}
+void RenderView::GetWorldMatrix(Double4x4& world) const
+{
+ world.M41 -= Origin.X;
+ world.M42 -= Origin.Y;
+ world.M43 -= Origin.Z;
+}
+
TaaJitterRemoveContext::TaaJitterRemoveContext(const RenderView& view)
{
if (view.IsTaaResolved)
diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h
index 0b9f931e7..63b86b155 100644
--- a/Source/Engine/Graphics/RenderView.h
+++ b/Source/Engine/Graphics/RenderView.h
@@ -5,6 +5,9 @@
#include "Engine/Core/Math/BoundingFrustum.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Math/Vector3.h"
+#if USE_LARGE_WORLDS
+#include "Engine/Core/Math/Double4x4.h"
+#endif
#include "Engine/Core/Types/LayersMask.h"
#include "Engine/Level/Types.h"
#include "Enums.h"
@@ -358,6 +361,9 @@ public:
world.M42 -= (float)Origin.Y;
world.M43 -= (float)Origin.Z;
}
+
+ // Applies the render origin to the transformation instance matrix.
+ void GetWorldMatrix(Double4x4& world) const;
};
// Removes TAA jitter from the RenderView when drawing geometry after TAA has been resolved to prevent unwanted jittering.
diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp
index 107e370e6..f66974720 100644
--- a/Source/Engine/Level/Actor.cpp
+++ b/Source/Engine/Level/Actor.cpp
@@ -15,6 +15,7 @@
#include "Engine/Content/Content.h"
#include "Engine/Core/Cache.h"
#include "Engine/Core/Collections/CollectionPoolCache.h"
+#include "Engine/Core/Math/Double4x4.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderView.h"
@@ -785,6 +786,27 @@ void Actor::GetLocalToWorldMatrix(Matrix& localToWorld) const
#endif
}
+void Actor::GetWorldToLocalMatrix(Double4x4& worldToLocal) const
+{
+ GetLocalToWorldMatrix(worldToLocal);
+ worldToLocal.Invert();
+}
+
+void Actor::GetLocalToWorldMatrix(Double4x4& localToWorld) const
+{
+#if 0
+ _transform.GetWorld(localToWorld);
+#else
+ _localTransform.GetWorld(localToWorld);
+ if (_parent)
+ {
+ Double4x4 parentToWorld;
+ _parent->GetLocalToWorldMatrix(parentToWorld);
+ localToWorld = localToWorld * parentToWorld;
+ }
+#endif
+}
+
void Actor::LinkPrefab(const Guid& prefabId, const Guid& prefabObjectId)
{
ASSERT(prefabId.IsValid());
diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h
index 9629c5b29..a6d8659fa 100644
--- a/Source/Engine/Level/Actor.h
+++ b/Source/Engine/Level/Actor.h
@@ -655,6 +655,18 @@ public:
/// The world to local matrix.
API_FUNCTION() void GetLocalToWorldMatrix(API_PARAM(Out) Matrix& localToWorld) const;
+ ///
+ /// Gets the matrix that transforms a point from the world space to local space of the actor.
+ ///
+ /// The world to local matrix.
+ void GetWorldToLocalMatrix(Double4x4& worldToLocal) const;
+
+ ///
+ /// Gets the matrix that transforms a point from the local space of the actor to world space.
+ ///
+ /// The world to local matrix.
+ void GetLocalToWorldMatrix(Double4x4& localToWorld) const;
+
public:
///
/// Gets actor bounding sphere that defines 3D space intersecting with the actor (for determination of the visibility for actor).
diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index 464d9aeda..54fe6119a 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -919,9 +919,7 @@ void AnimatedModel::Draw(RenderContext& renderContext)
return;
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
return; // No supported
- Matrix world;
- GetLocalToWorldMatrix(world);
- renderContext.View.GetWorldMatrix(world);
+ ACTOR_GET_WORLD_MATRIX(this, view, world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(_transform.Translation, renderContext.View.WorldPosition));
diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp
index 3840ab5ce..70c162fbe 100644
--- a/Source/Engine/Level/Actors/StaticModel.cpp
+++ b/Source/Engine/Level/Actors/StaticModel.cpp
@@ -334,9 +334,7 @@ void StaticModel::Draw(RenderContext& renderContext)
GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, this, _sphere, _transform, Model->LODs.Last().GetBox());
return;
}
- Matrix world;
- GetLocalToWorldMatrix(world);
- renderContext.View.GetWorldMatrix(world);
+ ACTOR_GET_WORLD_MATRIX(this, view, world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
if (_vertexColorsDirty)
FlushVertexColors();
@@ -372,9 +370,7 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
if (!Model || !Model->IsLoaded())
return;
const RenderContext& renderContext = renderContextBatch.GetMainContext();
- Matrix world;
- GetLocalToWorldMatrix(world);
- renderContext.View.GetWorldMatrix(world);
+ ACTOR_GET_WORLD_MATRIX(this, view, world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
if (_vertexColorsDirty)
FlushVertexColors();
diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h
index a3324e1f3..14d28014c 100644
--- a/Source/Engine/Renderer/DrawCall.h
+++ b/Source/Engine/Renderer/DrawCall.h
@@ -342,3 +342,9 @@ struct TIsPODType
drawState.PrevWorld = worldMatrix; \
drawState.PrevFrame = frame; \
}
+
+#if USE_LARGE_WORLDS
+#define ACTOR_GET_WORLD_MATRIX(actor, view, world) Real4x4 worldReal; actor->GetLocalToWorldMatrix(worldReal); renderContext.View.GetWorldMatrix(worldReal); Matrix world = (Matrix)worldReal;
+#else
+#define ACTOR_GET_WORLD_MATRIX(actor, view, world) Real4x4 world; actor->GetLocalToWorldMatrix(world); renderContext.View.GetWorldMatrix(world);
+#endif