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.