diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp
index 242c3ed81..10b9dba28 100644
--- a/Source/Engine/Content/Assets/SkinnedModel.cpp
+++ b/Source/Engine/Content/Assets/SkinnedModel.cpp
@@ -157,6 +157,11 @@ bool SkinnedModel::Intersects(const Ray& ray, const Matrix& world, Real& distanc
return LODs[lodIndex].Intersects(ray, world, distance, normal, mesh);
}
+bool SkinnedModel::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, SkinnedMesh** mesh, int32 lodIndex)
+{
+ return LODs[lodIndex].Intersects(ray, transform, distance, normal, mesh);
+}
+
BoundingBox SkinnedModel::GetBox(const Matrix& world, int32 lodIndex) const
{
return LODs[lodIndex].GetBox(world);
diff --git a/Source/Engine/Content/Assets/SkinnedModel.h b/Source/Engine/Content/Assets/SkinnedModel.h
index fd55ea4ed..60a811424 100644
--- a/Source/Engine/Content/Assets/SkinnedModel.h
+++ b/Source/Engine/Content/Assets/SkinnedModel.h
@@ -177,6 +177,18 @@ public:
/// True whether the two objects intersected
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, SkinnedMesh** mesh, int32 lodIndex = 0);
+ ///
+ /// Determines if there is an intersection between the SkinnedModel and a Ray in given world using given instance.
+ ///
+ /// The ray to test
+ /// Instance transformation
+ /// When the method completes, contains the distance of the intersection (if any valid).
+ /// When the method completes, contains the intersection surface normal vector (if any valid).
+ /// Mesh, or null
+ /// Level Of Detail index
+ /// True whether the two objects intersected
+ bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, SkinnedMesh** mesh, int32 lodIndex = 0);
+
///
/// Gets the model bounding box in custom matrix world space (rig pose transformed by matrix, not animated).
///
diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp
index 6783bac7a..6da1945a7 100644
--- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp
+++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp
@@ -127,6 +127,20 @@ bool SkinnedMesh::Intersects(const Ray& ray, const Matrix& world, Real& distance
return transformedBox.Intersects(ray, distance, normal);
}
+bool SkinnedMesh::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const
+{
+ // Transform points
+ Vector3 min, max;
+ transform.LocalToWorld(_box.Minimum, min);
+ transform.LocalToWorld(_box.Maximum, max);
+
+ // Get transformed box
+ BoundingBox transformedBox(min, max);
+
+ // Test ray on a box
+ return transformedBox.Intersects(ray, distance, normal);
+}
+
void SkinnedMesh::Render(GPUContext* context) const
{
ASSERT(IsInitialized());
diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h
index 08a9f8dbb..874bdb48a 100644
--- a/Source/Engine/Graphics/Models/SkinnedMesh.h
+++ b/Source/Engine/Graphics/Models/SkinnedMesh.h
@@ -150,6 +150,16 @@ public:
/// True whether the two objects intersected
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const;
+ ///
+ /// Determines if there is an intersection between the mesh and a ray in given world
+ ///
+ /// The ray to test
+ /// Instance transformation
+ /// When the method completes and returns true, contains the distance of the intersection (if any valid).
+ /// When the method completes, contains the intersection surface normal vector (if any valid).
+ /// True whether the two objects intersected
+ bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const;
+
///
/// Retrieves the eight corners of the bounding box.
///
diff --git a/Source/Engine/Graphics/Models/SkinnedModelLOD.cpp b/Source/Engine/Graphics/Models/SkinnedModelLOD.cpp
index e85e58fd7..539118a85 100644
--- a/Source/Engine/Graphics/Models/SkinnedModelLOD.cpp
+++ b/Source/Engine/Graphics/Models/SkinnedModelLOD.cpp
@@ -100,6 +100,31 @@ bool SkinnedModelLOD::Intersects(const Ray& ray, const Matrix& world, Real& dist
return result;
}
+bool SkinnedModelLOD::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, SkinnedMesh** mesh)
+{
+ // Check all meshes
+ bool result = false;
+ Real closest = MAX_float;
+ Vector3 closestNormal = Vector3::Up;
+ for (int32 i = 0; i < Meshes.Count(); i++)
+ {
+ // Test intersection with mesh and check if is closer than previous
+ Real dst;
+ Vector3 nrm;
+ if (Meshes[i].Intersects(ray, transform, dst, nrm) && dst < closest)
+ {
+ result = true;
+ *mesh = &Meshes[i];
+ closest = dst;
+ closestNormal = nrm;
+ }
+ }
+
+ distance = closest;
+ normal = closestNormal;
+ return result;
+}
+
BoundingBox SkinnedModelLOD::GetBox(const Matrix& world) const
{
// Find minimum and maximum points of all the meshes
diff --git a/Source/Engine/Graphics/Models/SkinnedModelLOD.h b/Source/Engine/Graphics/Models/SkinnedModelLOD.h
index fbddf0041..32d2f2ee5 100644
--- a/Source/Engine/Graphics/Models/SkinnedModelLOD.h
+++ b/Source/Engine/Graphics/Models/SkinnedModelLOD.h
@@ -63,6 +63,17 @@ public:
/// True whether the two objects intersected
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, SkinnedMesh** mesh);
+ ///
+ /// Determines if there is an intersection between the Model and a Ray in given world using given instance
+ ///
+ /// The ray to test
+ /// Instance transformation
+ /// When the method completes, contains the distance of the intersection (if any valid).
+ /// When the method completes, contains the intersection surface normal vector (if any valid).
+ /// Mesh, or null
+ /// True whether the two objects intersected
+ bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, SkinnedMesh** mesh);
+
///
/// Get model bounding box in transformed world for given instance buffer
///
diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index 3a0ab2641..97af41f2b 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -23,7 +23,6 @@ AnimatedModel::AnimatedModel(const SpawnParams& params)
, _lastUpdateFrame(0)
{
GraphInstance.Object = this;
- _world = Matrix::Identity;
UpdateBounds();
SkinnedModel.Changed.Bind(this);
@@ -122,7 +121,8 @@ void AnimatedModel::GetCurrentPose(Array& nodesTransformation, bool worl
nodesTransformation = GraphInstance.NodesPose;
if (worldSpace)
{
- const auto world = _world;
+ Matrix world;
+ _transform.GetWorld(world);
for (auto& m : nodesTransformation)
m = m * world;
}
@@ -136,8 +136,10 @@ void AnimatedModel::SetCurrentPose(const Array& nodesTransformation, boo
GraphInstance.NodesPose = nodesTransformation;
if (worldSpace)
{
+ Matrix world;
+ _transform.GetWorld(world);
Matrix invWorld;
- Matrix::Invert(_world, invWorld);
+ Matrix::Invert(world, invWorld);
for (auto& m : GraphInstance.NodesPose)
m = invWorld * m;
}
@@ -153,7 +155,11 @@ void AnimatedModel::GetNodeTransformation(int32 nodeIndex, Matrix& nodeTransform
else
nodeTransformation = Matrix::Identity;
if (worldSpace)
- nodeTransformation = nodeTransformation * _world;
+ {
+ Matrix world;
+ _transform.GetWorld(world);
+ nodeTransformation = nodeTransformation * world;
+ }
}
void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& nodeTransformation, bool worldSpace) const
@@ -545,7 +551,7 @@ void AnimatedModel::UpdateBounds()
{
UpdateLocalBounds();
- BoundingBox::Transform(_boxLocal, _world, _box);
+ BoundingBox::Transform(_boxLocal, _transform, _box);
BoundingSphere::FromBox(_box, _sphere);
if (_sceneRenderingKey != -1)
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
@@ -696,7 +702,9 @@ void AnimatedModel::Draw(RenderContext& renderContext)
return; // TODO: Animated Model rendering to Global SDF
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
return; // No supported
- GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, _world);
+ Matrix world;
+ renderContext.View.GetWorldMatrix(_transform, world);
+ GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass & (int32)renderContext.View.GetShadowsDrawPassMask(ShadowsMode));
if (SkinnedModel && SkinnedModel->IsLoaded() && drawModes != DrawPass::None)
@@ -708,7 +716,7 @@ void AnimatedModel::Draw(RenderContext& renderContext)
#if USE_EDITOR
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
if (!Editor::IsPlayMode)
- _drawState.PrevWorld = _world;
+ _drawState.PrevWorld = world;
#endif
_skinningData.Flush(GPUDevice::Instance->GetMainContext());
@@ -717,7 +725,7 @@ void AnimatedModel::Draw(RenderContext& renderContext)
draw.Buffer = &Entries;
draw.Skinning = &_skinningData;
draw.BlendShapes = &_blendShapes;
- draw.World = &_world;
+ draw.World = &world;
draw.DrawState = &_drawState;
draw.DrawModes = drawModes;
draw.Bounds = _sphere;
@@ -729,7 +737,7 @@ void AnimatedModel::Draw(RenderContext& renderContext)
}
}
- GEOMETRY_DRAW_STATE_EVENT_END(_drawState, _world);
+ GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world);
}
#if USE_EDITOR
@@ -760,7 +768,7 @@ bool AnimatedModel::IntersectsItself(const Ray& ray, Real& distance, Vector3& no
if (SkinnedModel != nullptr && SkinnedModel->IsLoaded())
{
SkinnedMesh* mesh;
- result |= SkinnedModel->Intersects(ray, _world, distance, normal, &mesh);
+ result |= SkinnedModel->Intersects(ray, _transform, distance, normal, &mesh);
}
return result;
@@ -833,7 +841,7 @@ bool AnimatedModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& dist
for (int32 i = 0; i < meshes.Count(); i++)
{
const auto& mesh = meshes[i];
- if (mesh.GetMaterialSlotIndex() == entryIndex && mesh.Intersects(ray, _world, distance, normal))
+ if (mesh.GetMaterialSlotIndex() == entryIndex && mesh.Intersects(ray, _transform, distance, normal))
return true;
}
@@ -860,7 +868,7 @@ bool AnimatedModel::IntersectsEntry(const Ray& ray, Real& distance, Vector3& nor
const auto& mesh = meshes[i];
Real dst;
Vector3 nrm;
- if (mesh.Intersects(ray, _world, dst, nrm) && dst < closest)
+ if (mesh.Intersects(ray, _transform, dst, nrm) && dst < closest)
{
result = true;
closest = dst;
@@ -880,8 +888,7 @@ void AnimatedModel::OnTransformChanged()
// Base
ModelInstanceActor::OnTransformChanged();
- _transform.GetWorld(_world);
- BoundingBox::Transform(_boxLocal, _world, _box);
+ BoundingBox::Transform(_boxLocal, _transform, _box);
BoundingSphere::FromBox(_box, _sphere);
if (_sceneRenderingKey != -1)
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h
index 63efa487a..176da0dd8 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.h
+++ b/Source/Engine/Level/Actors/AnimatedModel.h
@@ -55,7 +55,6 @@ public:
private:
BoundingBox _boxLocal;
- Matrix _world;
GeometryDrawStateData _drawState;
SkinnedMeshDrawData _skinningData;
AnimationUpdateMode _actualMode;
@@ -156,14 +155,6 @@ public:
///
AnimGraphInstanceData GraphInstance;
- ///
- /// Gets the model world matrix transform.
- ///
- FORCE_INLINE void GetWorld(Matrix* world) const
- {
- *world = _world;
- }
-
///
/// Resets the animation state (clears the instance state data but preserves the instance parameters values).
///
diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp
index f5a5d0f8c..01e261ff5 100644
--- a/Source/Engine/Level/Actors/SplineModel.cpp
+++ b/Source/Engine/Level/Actors/SplineModel.cpp
@@ -372,13 +372,14 @@ void SplineModel::Draw(RenderContext& renderContext)
drawCall.PerInstanceRandom = GetPerInstanceRandom();
_preTransform.GetWorld(drawCall.Deformable.LocalMatrix);
const Transform splineTransform = GetTransform();
- splineTransform.GetWorld(drawCall.World);
+ renderContext.View.GetWorldMatrix(splineTransform, drawCall.World);
drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation();
const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant();
for (int32 segment = 0; segment < _instances.Count(); segment++)
{
auto& instance = _instances[segment];
- if (!(renderContext.View.IsCullingDisabled || renderContext.View.CullingFrustum.Intersects(instance.Sphere)))
+ BoundingSphere instanceSphere(instance.Sphere.Center - renderContext.View.Origin, instance.Sphere.Radius);
+ if (!(renderContext.View.IsCullingDisabled || renderContext.View.CullingFrustum.Intersects(instanceSphere)))
continue;
drawCall.Deformable.Segment = (float)segment;
@@ -390,7 +391,7 @@ void SplineModel::Draw(RenderContext& renderContext)
}
else
{
- lodIndex = RenderTools::ComputeModelLOD(model, instance.Sphere.Center, (float)instance.Sphere.Radius, renderContext);
+ lodIndex = RenderTools::ComputeModelLOD(model, instanceSphere.Center, (float)instanceSphere.Radius, renderContext);
if (lodIndex == -1)
continue;
}
diff --git a/Source/Engine/Level/LargeWorlds.h b/Source/Engine/Level/LargeWorlds.h
index 56ecb2fb0..f39d0c11e 100644
--- a/Source/Engine/Level/LargeWorlds.h
+++ b/Source/Engine/Level/LargeWorlds.h
@@ -19,7 +19,7 @@ API_CLASS(Static) class FLAXENGINE_API LargeWorlds
///
/// Defines the size of a single chunk. Large world (64-bit) gets divided into smaller chunks so all the math operations (32-bit) can be performed relative to the chunk origin without precision loss.
///
- API_FIELD() static constexpr Real ChunkSize = 1024;
+ API_FIELD() static constexpr Real ChunkSize = 262144;
///
/// Updates the large world origin to match the input position. The origin is snapped to the best matching chunk location.