From 134c8b99aad51ac7d5510ad06e2e5e5d60b89df6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 21 Jun 2022 20:03:13 +0200 Subject: [PATCH] Add relative-to-camera rendering for large worlds --- Source/Editor/Tools/Foliage/FoliageTools.cpp | 13 ++--- .../Utilities/ViewportIconsRenderer.cpp | 4 +- Source/Editor/Viewport/EditorViewport.cs | 18 +++---- Source/Engine/Content/Assets/Model.cpp | 5 ++ Source/Engine/Content/Assets/Model.h | 23 ++++++++ Source/Engine/Core/Math/BoundingFrustum.cpp | 5 +- Source/Engine/Foliage/Foliage.cpp | 8 +-- .../Engine/Graphics/Models/CollisionProxy.h | 23 ++++++++ Source/Engine/Graphics/Models/Mesh.cpp | 34 +++++++++++- Source/Engine/Graphics/Models/Mesh.h | 13 ++--- Source/Engine/Graphics/Models/ModelLOD.cpp | 53 ++++++++++++------- Source/Engine/Graphics/Models/ModelLOD.h | 20 +++++-- Source/Engine/Graphics/RenderTask.cpp | 3 ++ Source/Engine/Graphics/RenderView.cpp | 33 +++++------- Source/Engine/Graphics/RenderView.cs | 23 +++----- Source/Engine/Graphics/RenderView.h | 21 +++++--- Source/Engine/Level/Actors/BoxVolume.cpp | 4 +- Source/Engine/Level/Actors/Camera.cpp | 40 ++++++++------ Source/Engine/Level/Actors/Camera.h | 31 ++++------- .../Engine/Level/Actors/DirectionalLight.cpp | 5 +- Source/Engine/Level/Actors/PointLight.cpp | 5 +- Source/Engine/Level/Actors/PostFxVolume.cpp | 2 +- Source/Engine/Level/Actors/SkyLight.cpp | 5 +- Source/Engine/Level/Actors/SplineModel.cpp | 2 +- Source/Engine/Level/Actors/SpotLight.cpp | 5 +- Source/Engine/Level/Actors/StaticModel.cpp | 38 ++++++------- Source/Engine/Level/Actors/StaticModel.h | 10 ---- Source/Engine/Level/LargeWorlds.h | 31 +++++++++++ Source/Engine/Level/Level.cpp | 14 +++++ Source/Engine/Level/Scene/SceneRendering.cpp | 7 ++- .../Engine/Physics/Colliders/BoxCollider.cpp | 2 +- .../Physics/Colliders/CapsuleCollider.cpp | 2 +- .../ShadowsOfMordor/Builder.Entries.cpp | 15 ++---- .../Engine/ShadowsOfMordor/Builder.Jobs.cpp | 2 +- Source/Engine/Terrain/TerrainChunk.cpp | 7 +-- 35 files changed, 331 insertions(+), 195 deletions(-) create mode 100644 Source/Engine/Level/LargeWorlds.h diff --git a/Source/Editor/Tools/Foliage/FoliageTools.cpp b/Source/Editor/Tools/Foliage/FoliageTools.cpp index 30bdb5dfd..30133d766 100644 --- a/Source/Editor/Tools/Foliage/FoliageTools.cpp +++ b/Source/Editor/Tools/Foliage/FoliageTools.cpp @@ -80,9 +80,10 @@ struct GeometryLookup PROFILE_CPU_NAMED("StaticModel"); // Check model meshes - Matrix world; - staticModel->GetWorld(&world); - const bool isDeterminantPositive = staticModel->GetTransform().GetDeterminant() >= 0.0f; + Transform transform = staticModel->GetTransform(); + Matrix worldMatrix; + transform.GetWorld(worldMatrix); + const bool isDeterminantPositive = transform.GetDeterminant() >= 0.0f; auto& lod = staticModel->Model->LODs[0]; for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++) { @@ -96,9 +97,9 @@ struct GeometryLookup // Transform triangle vertices from mesh space to world space Vector3 t0, t1, t2; - Vector3::Transform(t.V0, world, t0); - Vector3::Transform(t.V1, world, t1); - Vector3::Transform(t.V2, world, t2); + Vector3::Transform(t.V0, worldMatrix, t0); + Vector3::Transform(t.V1, worldMatrix, t1); + Vector3::Transform(t.V2, worldMatrix, t2); // Check if triangles intersects with the brush if (CollisionsHelper::SphereIntersectsTriangle(brush, t0, t1, t2)) diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index 450bc5052..8b8e0d74f 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -93,7 +93,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene Matrix m1, m2, world; for (Actor* icon : icons) { - BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS); + BoundingSphere sphere(icon->GetPosition() - renderContext.View.Origin, ICON_RADIUS); IconTypes iconType; if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType)) { @@ -120,7 +120,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor auto& view = renderContext.View; const BoundingFrustum frustum = view.Frustum; Matrix m1, m2, world; - BoundingSphere sphere(actor->GetPosition(), ICON_RADIUS); + BoundingSphere sphere(actor->GetPosition() - renderContext.View.Origin, ICON_RADIUS); IconTypes iconType; if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType)) { diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index ae89ed7df..110ab657c 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -777,16 +777,16 @@ namespace FlaxEditor.Viewport /// The view. public void CopyViewData(ref RenderView view) { - // Create matrices - CreateProjectionMatrix(out view.Projection); - CreateViewMatrix(out view.View); - - // Copy data - view.Position = ViewPosition; + Vector3 position = ViewPosition; + LargeWorlds.UpdateOrigin(ref view.Origin, position); + view.Position = position - view.Origin; view.Direction = ViewDirection; view.Near = _nearPlane; view.Far = _farPlane; + CreateProjectionMatrix(out view.Projection); + CreateViewMatrix(view.Position, out view.View); + view.UpdateCachedData(); } @@ -828,10 +828,10 @@ namespace FlaxEditor.Viewport /// /// Creates the view matrix. /// + /// The view position. /// The result. - protected virtual void CreateViewMatrix(out Matrix result) + protected virtual void CreateViewMatrix(Float3 position, out Matrix result) { - var position = (Float3)ViewPosition; // TODO: large-worlds var direction = ViewDirection; var target = position + direction; var right = Float3.Normalize(Float3.Cross(Float3.Up, direction)); @@ -862,7 +862,7 @@ namespace FlaxEditor.Viewport // Prepare var viewport = new FlaxEngine.Viewport(0, 0, Width, Height); CreateProjectionMatrix(out var p); - CreateViewMatrix(out var v); + CreateViewMatrix(Float3.Zero, out var v); // TODO: large-worlds Matrix.Multiply(ref v, ref p, out var ivp); ivp.Invert(); diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index 4dcdcf3e2..11ccd841a 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -172,6 +172,11 @@ bool Model::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vect return LODs[lodIndex].Intersects(ray, world, distance, normal, mesh); } +bool Model::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, Mesh** mesh, int32 lodIndex) +{ + return LODs[lodIndex].Intersects(ray, transform, distance, normal, mesh); +} + BoundingBox Model::GetBox(const Matrix& world, int32 lodIndex) const { return LODs[lodIndex].GetBox(world); diff --git a/Source/Engine/Content/Assets/Model.h b/Source/Engine/Content/Assets/Model.h index 9156582d2..dba0df965 100644 --- a/Source/Engine/Content/Assets/Model.h +++ b/Source/Engine/Content/Assets/Model.h @@ -134,6 +134,18 @@ public: /// True whether the two objects intersected bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh, int32 lodIndex = 0); + /// + /// Determines if there is an intersection between the Model and a Ray in given world using given instance. + /// + /// The ray to test + /// The 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, Mesh** mesh, int32 lodIndex = 0); + /// /// Gets the model bounding box in custom matrix world space. /// @@ -142,6 +154,17 @@ public: /// The bounding box. API_FUNCTION() BoundingBox GetBox(const Matrix& world, int32 lodIndex = 0) const; + /// + /// Gets the model bounding box in custom transformation. + /// + /// The instance transformation. + /// The Level Of Detail index. + /// The bounding box. + API_FUNCTION() BoundingBox GetBox(const Transform& transform, int32 lodIndex = 0) const + { + return LODs[lodIndex].GetBox(transform); + } + /// /// Gets the model bounding box in local space. /// diff --git a/Source/Engine/Core/Math/BoundingFrustum.cpp b/Source/Engine/Core/Math/BoundingFrustum.cpp index 88e3467c8..88f35b8ca 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.cpp +++ b/Source/Engine/Core/Math/BoundingFrustum.cpp @@ -134,8 +134,7 @@ float BoundingFrustum::GetHeightAtDepth(float depth) const ContainmentType BoundingFrustum::Contains(const Vector3& point) const { PlaneIntersectionType result = PlaneIntersectionType::Front; - PlaneIntersectionType planeResult = PlaneIntersectionType::Front; - for (int i = 0; i < 6; i++) + for (int32 i = 0; i < 6; i++) { const PlaneIntersectionType planeResult = _planes[i].Intersects(point); switch (planeResult) @@ -159,7 +158,7 @@ ContainmentType BoundingFrustum::Contains(const Vector3& point) const ContainmentType BoundingFrustum::Contains(const BoundingSphere& sphere) const { auto result = PlaneIntersectionType::Front; - for (int i = 0; i < 6; i++) + for (int32 i = 0; i < 6; i++) { const PlaneIntersectionType planeResult = _planes[i].Intersects(sphere); switch (planeResult) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 76056ac44..162742811 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -427,7 +427,7 @@ void Foliage::AddInstance(const FoliageInstance& instance) auto& meshes = type->Model->LODs[0].Meshes; for (int32 j = 0; j < meshes.Count(); j++) { - meshes[j].GetCorners(corners); + meshes[j].GetBox().GetCorners(corners); for (int32 k = 0; k < 8; k++) { @@ -469,7 +469,7 @@ void Foliage::SetInstanceTransform(int32 index, const Transform& value) auto& meshes = type->Model->LODs[0].Meshes; for (int32 j = 0; j < meshes.Count(); j++) { - meshes[j].GetCorners(corners); + meshes[j].GetBox().GetCorners(corners); for (int32 k = 0; k < 8; k++) { @@ -512,7 +512,7 @@ void Foliage::OnFoliageTypeModelLoaded(int32 index) for (int32 j = 0; j < meshes.Count(); j++) { // TODO: cache bounds for all model meshes and reuse later - meshes[j].GetCorners(corners); + meshes[j].GetBox().GetCorners(corners); // TODO: use SIMD for (int32 k = 0; k < 8; k++) @@ -1282,7 +1282,7 @@ void Foliage::OnTransformChanged() auto& meshes = type->Model->LODs[0].Meshes; for (int32 j = 0; j < meshes.Count(); j++) { - meshes[j].GetCorners(corners); + meshes[j].GetBox().GetCorners(corners); for (int32 k = 0; k < 8; k++) { diff --git a/Source/Engine/Graphics/Models/CollisionProxy.h b/Source/Engine/Graphics/Models/CollisionProxy.h index 346eb8e25..d19f43949 100644 --- a/Source/Engine/Graphics/Models/CollisionProxy.h +++ b/Source/Engine/Graphics/Models/CollisionProxy.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Core/Math/Vector3.h" +#include "Engine/Core/Math/Transform.h" #include "Engine/Core/Math/Ray.h" #include "Engine/Core/Math/CollisionsHelper.h" #include "Engine/Core/Collections/Array.h" @@ -74,4 +75,26 @@ public: } return false; } + + bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const + { + for (int32 i = 0; i < Triangles.Count(); i++) + { + CollisionTriangle triangle = Triangles[i]; + Vector3 v0, v1, v2; + transform.LocalToWorld(triangle.V0, v0); + transform.LocalToWorld(triangle.V1, v1); + transform.LocalToWorld(triangle.V2, v2); + + // TODO: use 32-bit precision for intersection + Real d; + if (CollisionsHelper::RayIntersectsTriangle(ray, triangle.V0, triangle.V1, triangle.V2, d)) + { + normal = Vector3::Normalize((triangle.V1 - triangle.V0) ^ (triangle.V2 - triangle.V0)); + distance = d; + return true; + } + } + return false; + } }; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index c39125d04..dc847a471 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -4,6 +4,7 @@ #include "ModelInstanceEntry.h" #include "Engine/Content/Assets/Material.h" #include "Engine/Content/Assets/Model.h" +#include "Engine/Core/Math/Transform.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/RenderTask.h" @@ -319,7 +320,7 @@ bool Mesh::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vecto { // Get bounding box of the mesh bounds transformed by the instance world matrix Vector3 corners[8]; - GetCorners(corners); + _box.GetCorners(corners); Vector3 tmp; Vector3::Transform(corners[0], world, tmp); Vector3 min = tmp; @@ -339,7 +340,38 @@ bool Mesh::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vecto // Use exact test on raw geometry return _collisionProxy.Intersects(ray, world, distance, normal); } + distance = 0; + normal = Vector3::Up; + return false; +#else + return transformedBox.Intersects(ray, distance, normal); +#endif +} +bool Mesh::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const +{ + // Get bounding box of the mesh bounds transformed by the instance world matrix + Vector3 corners[8]; + _box.GetCorners(corners); + Vector3 tmp; + transform.LocalToWorld(corners[0], tmp); + Vector3 min = tmp; + Vector3 max = tmp; + for (int32 i = 1; i < 8; i++) + { + transform.LocalToWorld(corners[i], tmp); + min = Vector3::Min(min, tmp); + max = Vector3::Max(max, tmp); + } + const BoundingBox transformedBox(min, max); + + // Test ray on box +#if USE_PRECISE_MESH_INTERSECTS + if (transformedBox.Intersects(ray, distance)) + { + // Use exact test on raw geometry + return _collisionProxy.Intersects(ray, transform, distance, normal); + } distance = 0; normal = Vector3::Up; return false; diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 9d1dd01e0..5be9ea018 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -259,13 +259,14 @@ public: bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const; /// - /// Retrieves the eight corners of the bounding box. + /// Determines if there is an intersection between the mesh and a ray in given world /// - /// An array of points representing the eight corners of the bounding box. - FORCE_INLINE void GetCorners(Vector3 corners[8]) const - { - _box.GetCorners(corners); - } + /// The ray to test + /// The 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; public: /// diff --git a/Source/Engine/Graphics/Models/ModelLOD.cpp b/Source/Engine/Graphics/Models/ModelLOD.cpp index a6b6e9980..ec71f7b7e 100644 --- a/Source/Engine/Graphics/Models/ModelLOD.cpp +++ b/Source/Engine/Graphics/Models/ModelLOD.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. #include "ModelLOD.h" +#include "Engine/Core/Math/Transform.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Serialization/MemoryReadStream.h" @@ -60,13 +61,11 @@ void ModelLOD::Dispose() bool ModelLOD::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh) { - // Check all meshes bool result = false; Real closest = MAX_Real; 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, world, dst, nrm) && dst < closest) @@ -77,7 +76,28 @@ bool ModelLOD::Intersects(const Ray& ray, const Matrix& world, Real& distance, V closestNormal = nrm; } } + distance = closest; + normal = closestNormal; + return result; +} +bool ModelLOD::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, Mesh** mesh) +{ + bool result = false; + Real closest = MAX_Real; + Vector3 closestNormal = Vector3::Up; + for (int32 i = 0; i < Meshes.Count(); i++) + { + 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; @@ -85,14 +105,12 @@ bool ModelLOD::Intersects(const Ray& ray, const Matrix& world, Real& distance, V BoundingBox ModelLOD::GetBox(const Matrix& world) const { - // Find minimum and maximum points of all the meshes Vector3 tmp, min = Vector3::Maximum, max = Vector3::Minimum; Vector3 corners[8]; for (int32 j = 0; j < Meshes.Count(); j++) { const auto& mesh = Meshes[j]; - mesh.GetCorners(corners); - + mesh.GetBox().GetCorners(corners); for (int32 i = 0; i < 8; i++) { Vector3::Transform(corners[i], world, tmp); @@ -100,42 +118,39 @@ BoundingBox ModelLOD::GetBox(const Matrix& world) const max = Vector3::Max(max, tmp); } } - return BoundingBox(min, max); } -BoundingBox ModelLOD::GetBox(const Matrix& world, int32 meshIndex) const +BoundingBox ModelLOD::GetBox(const Transform& transform) const { - // Find minimum and maximum points of the mesh Vector3 tmp, min = Vector3::Maximum, max = Vector3::Minimum; Vector3 corners[8]; - const auto& mesh = Meshes[meshIndex]; - mesh.GetCorners(corners); - for (int32 i = 0; i < 8; i++) + for (int32 j = 0; j < Meshes.Count(); j++) { - Vector3::Transform(corners[i], world, tmp); - min = Vector3::Min(min, tmp); - max = Vector3::Max(max, tmp); + const auto& mesh = Meshes[j]; + mesh.GetBox().GetCorners(corners); + for (int32 i = 0; i < 8; i++) + { + transform.LocalToWorld(corners[i], tmp); + min = Vector3::Min(min, tmp); + max = Vector3::Max(max, tmp); + } } - return BoundingBox(min, max); } BoundingBox ModelLOD::GetBox() const { - // Find minimum and maximum points of the mesh in given world Vector3 min = Vector3::Maximum, max = Vector3::Minimum; Vector3 corners[8]; for (int32 j = 0; j < Meshes.Count(); j++) { - Meshes[j].GetCorners(corners); - + Meshes[j].GetBox().GetCorners(corners); for (int32 i = 0; i < 8; i++) { min = Vector3::Min(min, corners[i]); max = Vector3::Max(max, corners[i]); } } - return BoundingBox(min, max); } diff --git a/Source/Engine/Graphics/Models/ModelLOD.h b/Source/Engine/Graphics/Models/ModelLOD.h index 745f3ac16..fb3363ecc 100644 --- a/Source/Engine/Graphics/Models/ModelLOD.h +++ b/Source/Engine/Graphics/Models/ModelLOD.h @@ -79,19 +79,29 @@ public: bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh); /// - /// Get model bounding box in transformed world for given instance buffer + /// Determines if there is an intersection between the Model and a Ray in given world using given instance + /// + /// The ray to test + /// The 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, Mesh** mesh); + + /// + /// Get model bounding box in transformed world matrix. /// /// World matrix /// Bounding box BoundingBox GetBox(const Matrix& world) const; /// - /// Get model bounding box in transformed world for given instance buffer for only one mesh + /// Get model bounding box in transformed world. /// - /// World matrix - /// esh index + /// The instance transformation. /// Bounding box - BoundingBox GetBox(const Matrix& world, int32 meshIndex) const; + BoundingBox GetBox(const Transform& transform) const; /// /// Gets the bounding box combined for all meshes in this model LOD. diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index c93743480..6e6a9a919 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -255,6 +255,9 @@ void SceneRenderTask::ClearCustomActors() void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext) { + // Cache WorldPosition used for PostFx volumes blending (RenderView caches it later on) + renderContext.View.WorldPosition = renderContext.View.Origin + renderContext.View.Position; + if ((ActorsSource & ActorsSources::Scenes) != 0) { Level::CollectPostFxVolumes(renderContext); diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp index d5f77e8b1..574add86d 100644 --- a/Source/Engine/Graphics/RenderView.cpp +++ b/Source/Engine/Graphics/RenderView.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. #include "RenderView.h" +#include "Engine/Level/LargeWorlds.h" #include "Engine/Level/Actors/Camera.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/RendererPass.h" @@ -70,6 +71,8 @@ void RenderView::PrepareCache(RenderContext& renderContext, float width, float h TemporalAAJitter.X = temporalAAJitter.X; TemporalAAJitter.Y = temporalAAJitter.Y; + WorldPosition = Origin + Position; + // Ortho views have issues with screen size LOD culling const float modelLODDistanceFactor = (renderContext.LodProxyView ? renderContext.LodProxyView->IsOrthographicProjection() : IsOrthographicProjection()) ? 100.0f : ModelLODDistanceFactor; ModelLODDistanceFactorSqrt = modelLODDistanceFactor * modelLODDistanceFactor; @@ -161,31 +164,15 @@ void RenderView::SetProjector(float nearPlane, float farPlane, const Float3& pos CullingFrustum = Frustum; } -void RenderView::CopyFrom(Camera* camera) -{ - Position = camera->GetPosition(); - Direction = camera->GetDirection(); - Near = camera->GetNearPlane(); - Far = camera->GetFarPlane(); - View = camera->GetView(); - Projection = camera->GetProjection(); - NonJitteredProjection = Projection; - Frustum = camera->GetFrustum(); - Matrix::Invert(View, IV); - Matrix::Invert(Projection, IP); - Frustum.GetInvMatrix(IVP); - CullingFrustum = Frustum; - RenderLayersMask = camera->RenderLayersMask; -} - void RenderView::CopyFrom(Camera* camera, Viewport* viewport) { - Position = camera->GetPosition(); + const Vector3 cameraPos = camera->GetPosition(); + LargeWorlds::UpdateOrigin(Origin, cameraPos); + Position = cameraPos - Origin; Direction = camera->GetDirection(); Near = camera->GetNearPlane(); Far = camera->GetFarPlane(); - View = camera->GetView(); - camera->GetMatrices(View, Projection, *viewport); + camera->GetMatrices(View, Projection, viewport ? *viewport : camera->GetViewport(), Origin); Frustum.SetMatrix(View, Projection); NonJitteredProjection = Projection; Matrix::Invert(View, IV); @@ -211,3 +198,9 @@ DrawPass RenderView::GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) cons return DrawPass::All; } } + +void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const +{ + const Vector3 translation = transform.Translation - Origin; + Matrix::Transformation(transform.Scale, transform.Orientation, translation, world); +} diff --git a/Source/Engine/Graphics/RenderView.cs b/Source/Engine/Graphics/RenderView.cs index e9b1015ae..7b54ab9ba 100644 --- a/Source/Engine/Graphics/RenderView.cs +++ b/Source/Engine/Graphics/RenderView.cs @@ -79,31 +79,24 @@ namespace FlaxEngine /// The camera. public void CopyFrom(Camera camera) { - Position = camera.Position; - Direction = camera.Direction; - Near = camera.NearPlane; - Far = camera.FarPlane; - View = camera.View; - Projection = camera.Projection; - NonJitteredProjection = Projection; - TemporalAAJitter = Float4.Zero; - RenderLayersMask = camera.RenderLayersMask; - - UpdateCachedData(); + var viewport = camera.Viewport; + CopyFrom(camera, ref viewport); } /// /// Copies render view data from the camera. /// /// The camera. - /// The custom viewport to use for view/projeection matrices override. - public void CopyFrom(Camera camera, ref Viewport customViewport) + /// The custom viewport to use for view/projeection matrices override. + public void CopyFrom(Camera camera, ref Viewport viewport) { - Position = camera.Position; + Vector3 cameraPos = camera.Position; + LargeWorlds.UpdateOrigin(ref Origin, cameraPos); + Position = cameraPos - Origin; Direction = camera.Direction; Near = camera.NearPlane; Far = camera.FarPlane; - camera.GetMatrices(out View, out Projection, ref customViewport); + camera.GetMatrices(out View, out Projection, ref viewport, ref Origin); NonJitteredProjection = Projection; TemporalAAJitter = Float4.Zero; RenderLayersMask = camera.RenderLayersMask; diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h index b06befda3..e4603d51e 100644 --- a/Source/Engine/Graphics/RenderView.h +++ b/Source/Engine/Graphics/RenderView.h @@ -24,7 +24,17 @@ API_STRUCT() struct FLAXENGINE_API RenderView DECLARE_SCRIPTING_TYPE_MINIMAL(RenderView); /// - /// The position of the view. + /// The position of the view origin (in world-units). Used for camera-relative rendering to achieve large worlds support with keeping 32-bit precision for coordinates in scene rendering. + /// + API_FIELD() Vector3 Origin = Vector3::Zero; + + /// + /// The global position of the view (Origin+Position). + /// + API_FIELD() Vector3 WorldPosition; + + /// + /// The position of the view (relative to the origin). /// API_FIELD() Float3 Position; @@ -264,14 +274,10 @@ public: /// Camera's FOV angle (in degrees) void SetProjector(float nearPlane, float farPlane, const Float3& position, const Float3& direction, const Float3& up, float angle); - // Copy view data from camera - // @param camera Camera to copy its data - void CopyFrom(Camera* camera); - // Copy view data from camera // @param camera Camera to copy its data // @param camera The custom viewport to use for view/projection matrices override. - void CopyFrom(Camera* camera, Viewport* viewport); + void CopyFrom(Camera* camera, Viewport* viewport = nullptr); public: DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const; @@ -288,4 +294,7 @@ public: { return Frustum.GetMatrix(); } + + // Calculates the world matrix for the given transformation instance rendering. + void GetWorldMatrix(const Transform& transform, Matrix& world) const; }; diff --git a/Source/Engine/Level/Actors/BoxVolume.cpp b/Source/Engine/Level/Actors/BoxVolume.cpp index 824745fd5..b7684ad90 100644 --- a/Source/Engine/Level/Actors/BoxVolume.cpp +++ b/Source/Engine/Level/Actors/BoxVolume.cpp @@ -17,7 +17,7 @@ void BoxVolume::SetSize(const Vector3& value) const auto prevBounds = _box; _size = value; OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds); - _bounds.Transform(_transform.GetWorld()); + _bounds.Transform(_transform); _bounds.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); OnBoundsChanged(prevBounds); @@ -142,7 +142,7 @@ void BoxVolume::OnTransformChanged() const auto prevBounds = _box; OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds); - _bounds.Transform(_transform.GetWorld()); + _bounds.Transform(_transform); _bounds.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); OnBoundsChanged(prevBounds); diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp index 6a6c5626b..673af868e 100644 --- a/Source/Engine/Level/Actors/Camera.cpp +++ b/Source/Engine/Level/Actors/Camera.cpp @@ -189,10 +189,15 @@ Viewport Camera::GetViewport() const void Camera::GetMatrices(Matrix& view, Matrix& projection) const { - GetMatrices(view, projection, GetViewport()); + GetMatrices(view, projection, GetViewport(), Vector3::Zero); } void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewport) const +{ + GetMatrices(view, projection, viewport, Vector3::Zero); +} + +void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewport, const Vector3& origin) const { // Create projection matrix if (_usePerspective) @@ -207,10 +212,11 @@ void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewp // Create view matrix const Float3 direction = GetDirection(); - const Vector3 target = _transform.Translation + direction; - Vector3 up; - Vector3::Transform(Vector3::Up, GetOrientation(), up); - Matrix::LookAt(_transform.Translation, target, up, view); + const Float3 position = _transform.Translation - origin; + const Float3 target = position + direction; + Float3 up; + Float3::Transform(Float3::Up, GetOrientation(), up); + Matrix::LookAt(position, target, up, view); } #if USE_EDITOR @@ -253,16 +259,19 @@ void Camera::Draw(RenderContext& renderContext) && _previewModel && _previewModel->IsLoaded()) { + Matrix world; + renderContext.View.GetWorldMatrix(_transform, world); GeometryDrawStateData drawState; Mesh::DrawInfo draw; draw.Buffer = &_previewModelBuffer; - draw.World = &_previewModelWorld; + draw.World = &world; draw.DrawState = &drawState; draw.Lightmap = nullptr; draw.LightmapUVs = nullptr; draw.Flags = StaticFlags::Transform; draw.DrawModes = (DrawPass)((DrawPass::Depth | DrawPass::GBuffer | DrawPass::Forward) & renderContext.View.Pass); BoundingSphere::FromBox(_previewModelBox, draw.Bounds); + draw.Bounds.Center -= renderContext.View.Origin; draw.PerInstanceRandom = GetPerInstanceRandom(); draw.LODBias = 0; draw.ForcedLOD = -1; @@ -288,32 +297,33 @@ void Camera::OnDebugDrawSelected() void Camera::UpdateCache() { - // Update view and projection matrix - GetMatrices(_view, _projection); + // Calculate view and projection matrices + Matrix view, projection; + GetMatrices(view, projection); // Update frustum and bounding box - _frustum.SetMatrix(_view, _projection); + _frustum.SetMatrix(view, projection); _frustum.GetBox(_box); BoundingSphere::FromBox(_box, _sphere); #if USE_EDITOR // Update editor preview model cache - Matrix rot, world; - _transform.GetWorld(world); + Matrix rot, tmp, world; + _transform.GetWorld(tmp); Matrix::RotationY(PI * -0.5f, rot); - Matrix::Multiply(rot, world, _previewModelWorld); + Matrix::Multiply(rot, tmp, world); // Calculate snap box for preview model if (_previewModel && _previewModel->IsLoaded()) { - _previewModelBox = _previewModel->GetBox(_previewModelWorld); + _previewModelBox = _previewModel->GetBox(world); } else { Vector3 min(-10.0f), max(10.0f); - min = Vector3::Transform(min, _previewModelWorld); - max = Vector3::Transform(max, _previewModelWorld); + min = Vector3::Transform(min, world); + max = Vector3::Transform(max, world); _previewModelBox = BoundingBox(min, max); } diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index 9c380dcfc..2b59ab323 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -35,7 +35,6 @@ API_CLASS(Sealed) class FLAXENGINE_API Camera : public Actor API_PROPERTY() static Camera* GetMainCamera(); private: - Matrix _view, _projection; BoundingFrustum _frustum; // Camera Settings @@ -50,27 +49,10 @@ private: AssetReference _previewModel; ModelInstanceEntries _previewModelBuffer; BoundingBox _previewModelBox; - Matrix _previewModelWorld; int32 _sceneRenderingKey = -1; #endif public: - /// - /// Gets the view matrix. - /// - API_PROPERTY() FORCE_INLINE Matrix GetView() const - { - return _view; - } - - /// - /// Gets the projection matrix. - /// - API_PROPERTY() FORCE_INLINE Matrix GetProjection() const - { - return _projection; - } - /// /// Gets the frustum. /// @@ -218,8 +200,17 @@ public: /// /// The result camera view matrix. /// The result camera projection matrix. - /// The custom output viewport. Use null to skip it. - API_FUNCTION() virtual void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport) const; + /// The custom output viewport. + API_FUNCTION() void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport) const; + + /// + /// Calculates the view and the projection matrices for the camera. Support using custom viewport and view origin. + /// + /// The result camera view matrix. + /// The result camera projection matrix. + /// The custom output viewport. + /// The rendering view origin (for relative-to-camera rendering). + API_FUNCTION() void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport, API_PARAM(Ref) const Vector3& origin) const; #if USE_EDITOR // Intersection check for editor picking the camera diff --git a/Source/Engine/Level/Actors/DirectionalLight.cpp b/Source/Engine/Level/Actors/DirectionalLight.cpp index 5b8d1d51f..59c0213a7 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.cpp +++ b/Source/Engine/Level/Actors/DirectionalLight.cpp @@ -18,13 +18,14 @@ void DirectionalLight::Draw(RenderContext& renderContext) { float brightness = Brightness; AdjustBrightness(renderContext.View, brightness); + const Float3 position = GetPosition() - renderContext.View.Origin; if (Brightness > ZeroTolerance && (renderContext.View.Flags & ViewFlags::DirectionalLights) != 0 && renderContext.View.Pass & DrawPass::GBuffer - && (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance)) + && (ViewDistance < ZeroTolerance || Float3::DistanceSquared(renderContext.View.Position, position) < ViewDistance * ViewDistance)) { RendererDirectionalLightData data; - data.Position = GetPosition(); // TODO: large-worlds + data.Position = position; data.MinRoughness = MinRoughness; data.ShadowsDistance = ShadowsDistance; data.Color = Color.ToFloat3() * (Color.A * brightness); diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp index 02eb4b517..af0d77e70 100644 --- a/Source/Engine/Level/Actors/PointLight.cpp +++ b/Source/Engine/Level/Actors/PointLight.cpp @@ -103,15 +103,16 @@ void PointLight::Draw(RenderContext& renderContext) { float brightness = ComputeBrightness(); AdjustBrightness(renderContext.View, brightness); + const Float3 position = GetPosition() - renderContext.View.Origin; const float radius = GetScaledRadius(); if ((renderContext.View.Flags & ViewFlags::PointLights) != 0 && brightness > ZeroTolerance && renderContext.View.Pass & DrawPass::GBuffer && radius > ZeroTolerance - && (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance)) + && (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, position) < ViewDistance * ViewDistance)) { RendererPointLightData data; - data.Position = GetPosition(); // TODO: large-worlds + data.Position = position; data.MinRoughness = MinRoughness; data.ShadowsDistance = ShadowsDistance; data.Color = Color.ToFloat3() * (Color.A * brightness); diff --git a/Source/Engine/Level/Actors/PostFxVolume.cpp b/Source/Engine/Level/Actors/PostFxVolume.cpp index 9ac6da649..4f46a7236 100644 --- a/Source/Engine/Level/Actors/PostFxVolume.cpp +++ b/Source/Engine/Level/Actors/PostFxVolume.cpp @@ -22,7 +22,7 @@ void PostFxVolume::Collect(RenderContext& renderContext) if (_isBounded) { Real distance; - if (_bounds.Contains(renderContext.View.Position, &distance) == ContainmentType::Contains) + if (_bounds.Contains(renderContext.View.WorldPosition, &distance) == ContainmentType::Contains) { if (_blendRadius > 0.0f) weight = Math::Saturate((float)distance / _blendRadius) * weight; diff --git a/Source/Engine/Level/Actors/SkyLight.cpp b/Source/Engine/Level/Actors/SkyLight.cpp index 9a86ff704..2261b72bb 100644 --- a/Source/Engine/Level/Actors/SkyLight.cpp +++ b/Source/Engine/Level/Actors/SkyLight.cpp @@ -100,13 +100,14 @@ void SkyLight::Draw(RenderContext& renderContext) { float brightness = Brightness; AdjustBrightness(renderContext.View, brightness); + const Float3 position = GetPosition() - renderContext.View.Origin; if ((renderContext.View.Flags & ViewFlags::SkyLights) != 0 && renderContext.View.Pass & DrawPass::GBuffer && brightness > ZeroTolerance - && (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance)) + && (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, position) < ViewDistance * ViewDistance)) { RendererSkyLightData data; - data.Position = GetPosition(); // TODO: large-worlds + data.Position = position; data.Color = Color.ToFloat3() * (Color.A * brightness); data.VolumetricScatteringIntensity = VolumetricScatteringIntensity; data.CastVolumetricShadow = CastVolumetricShadow; diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 6fc12edd6..f5a5d0f8c 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -136,7 +136,7 @@ void SplineModel::OnSplineUpdated() for (int32 j = 0; j < meshes.Count(); j++) { const auto& mesh = meshes[j]; - mesh.GetCorners(corners); + mesh.GetBox().GetCorners(corners); for (int32 i = 0; i < 8; i++) { diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp index a01a6e584..ad69b1502 100644 --- a/Source/Engine/Level/Actors/SpotLight.cpp +++ b/Source/Engine/Level/Actors/SpotLight.cpp @@ -151,6 +151,7 @@ void SpotLight::Draw(RenderContext& renderContext) { float brightness = ComputeBrightness(); AdjustBrightness(renderContext.View, brightness); + const Float3 position = GetPosition() - renderContext.View.Origin; const float radius = GetScaledRadius(); const float outerConeAngle = GetOuterConeAngle(); if ((renderContext.View.Flags & ViewFlags::SpotLights) != 0 @@ -158,10 +159,10 @@ void SpotLight::Draw(RenderContext& renderContext) && brightness > ZeroTolerance && radius > ZeroTolerance && outerConeAngle > ZeroTolerance - && (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, GetPosition()) < ViewDistance * ViewDistance)) + && (ViewDistance < ZeroTolerance || Vector3::DistanceSquared(renderContext.View.Position, position) < ViewDistance * ViewDistance)) { RendererSpotLightData data; - data.Position = GetPosition(); // TODO: large-worlds + data.Position = position; data.MinRoughness = MinRoughness; data.ShadowsDistance = ShadowsDistance; data.Color = Color.ToFloat3() * (Color.A * brightness); diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index 6bb0ed917..85026b05d 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -18,7 +18,6 @@ StaticModel::StaticModel(const SpawnParams& params) : ModelInstanceActor(params) - , _world(Matrix::Identity) , _scaleInLightmap(1.0f) , _boundsScale(1.0f) , _lodBias(0) @@ -210,16 +209,9 @@ void StaticModel::UpdateBounds() { if (Model && Model->IsLoaded()) { - Matrix world; - if (Math::IsOne(_boundsScale)) - world = _world; - else - { - Transform t = _transform; - t.Scale *= _boundsScale; - t.GetWorld(world); - } - _box = Model->GetBox(world); + Transform transform = _transform; + transform.Scale *= _boundsScale; + _box = Model->GetBox(transform); } else { @@ -240,17 +232,21 @@ void StaticModel::Draw(RenderContext& renderContext) const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass); if (!Model || !Model->IsLoaded() || !Model->CanBeRendered() || drawModes == DrawPass::None) return; + + Matrix world; + renderContext.View.GetWorldMatrix(_transform, world); + if (renderContext.View.Pass == DrawPass::GlobalSDF) { - GlobalSignDistanceFieldPass::Instance()->RasterizeModelSDF(this, Model->SDF, _world, _box); + GlobalSignDistanceFieldPass::Instance()->RasterizeModelSDF(this, Model->SDF, world, _box); return; } if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas) { - GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, this, _sphere, _world, Model->LODs.Last().GetBox()); + GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, this, _sphere, world, Model->LODs.Last().GetBox()); return; } - GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, _world); + GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world); // Flush vertex colors if need to if (_vertexColorsDirty) @@ -282,18 +278,19 @@ void StaticModel::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 Mesh::DrawInfo draw; draw.Buffer = &Entries; - draw.World = &_world; + draw.World = &world; draw.DrawState = &_drawState; draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); draw.LightmapUVs = &Lightmap.UVsArea; draw.Flags = _staticFlags; draw.DrawModes = drawModes; draw.Bounds = _sphere; + draw.Bounds.Center -= renderContext.View.Origin; draw.PerInstanceRandom = GetPerInstanceRandom(); draw.LODBias = _lodBias; draw.ForcedLOD = _forcedLod; @@ -301,7 +298,7 @@ void StaticModel::Draw(RenderContext& renderContext) Model->Draw(renderContext, draw); - GEOMETRY_DRAW_STATE_EVENT_END(_drawState, _world); + GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world); } bool StaticModel::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) @@ -311,7 +308,7 @@ bool StaticModel::IntersectsItself(const Ray& ray, Real& distance, Vector3& norm if (Model != nullptr && Model->IsLoaded()) { Mesh* mesh; - result = Model->Intersects(ray, _world, distance, normal, &mesh); + result = Model->Intersects(ray, _transform, distance, normal, &mesh); } return result; @@ -473,7 +470,7 @@ bool StaticModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distan 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; } @@ -500,7 +497,7 @@ bool StaticModel::IntersectsEntry(const Ray& ray, Real& distance, Vector3& norma 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; @@ -520,7 +517,6 @@ void StaticModel::OnTransformChanged() // Base ModelInstanceActor::OnTransformChanged(); - _transform.GetWorld(_world); UpdateBounds(); } diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h index 2068c2384..f3c5c4bb5 100644 --- a/Source/Engine/Level/Actors/StaticModel.h +++ b/Source/Engine/Level/Actors/StaticModel.h @@ -14,7 +14,6 @@ API_CLASS() class FLAXENGINE_API StaticModel : public ModelInstanceActor { DECLARE_SCENE_OBJECT(StaticModel); private: - Matrix _world; GeometryDrawStateData _drawState; float _scaleInLightmap; float _boundsScale; @@ -50,15 +49,6 @@ public: LightmapEntry Lightmap; public: - /// - /// Gets the model world matrix transform. - /// - /// The result world matrix. - FORCE_INLINE void GetWorld(Matrix* world) const - { - *world = _world; - } - /// /// Gets the model scale in lightmap (applied to all the meshes). /// diff --git a/Source/Engine/Level/LargeWorlds.h b/Source/Engine/Level/LargeWorlds.h new file mode 100644 index 000000000..56ecb2fb0 --- /dev/null +++ b/Source/Engine/Level/LargeWorlds.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" + +/// +/// The engine utility for large worlds support. Contains constants and tools for using 64-bit precision coordinates in various game systems (eg. scene rendering). +/// +API_CLASS(Static) class FLAXENGINE_API LargeWorlds +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(LargeWorlds); + + /// + /// Enables large worlds usage in the engine. If true, scene rendering and other systems will use origin shifting to achieve higher precision in large worlds. + /// + API_FIELD() static bool Enable; + + /// + /// 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; + + /// + /// Updates the large world origin to match the input position. The origin is snapped to the best matching chunk location. + /// + /// The current origin of teh large world. Gets updated with the input position. + /// The current input position (eg. render view location or auto listener position). + /// Used only if LargeWorlds::Enabled is true. + API_FUNCTION() static void UpdateOrigin(API_PARAM(Ref) Vector3& origin, const Vector3& position); +}; diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index ddcc7797a..0e35e9dac 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -2,6 +2,7 @@ #include "Level.h" #include "ActorsCache.h" +#include "LargeWorlds.h" #include "SceneQuery.h" #include "SceneObjectsFactory.h" #include "Scene/Scene.h" @@ -39,6 +40,19 @@ #include "Engine/Serialization/JsonSerializer.h" #endif +#if USE_LARGE_WORLDS +bool LargeWorlds::Enable = true; +#else +bool LargeWorlds::Enable = false; +#endif + +void LargeWorlds::UpdateOrigin(Vector3& origin, const Vector3& position) +{ + if (Enable) + { + } +} + bool LayersMask::HasLayer(const StringView& layerName) const { return HasLayer(Level::GetLayerIndex(layerName)); diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp index affd8e576..8dcde5afc 100644 --- a/Source/Engine/Level/Scene/SceneRendering.cpp +++ b/Source/Engine/Level/Scene/SceneRendering.cpp @@ -33,6 +33,7 @@ void SceneRendering::Draw(RenderContext& renderContext) ScopeLock lock(Locker); auto& view = renderContext.View; const BoundingFrustum frustum = view.CullingFrustum; + const Vector3 origin = view.Origin; renderContext.List->Scenes.Add(this); // Draw all visual components @@ -40,7 +41,8 @@ void SceneRendering::Draw(RenderContext& renderContext) { for (int32 i = 0; i < Actors.Count(); i++) { - auto& e = Actors[i]; + auto e = Actors[i]; + e.Bounds.Center -= origin; if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds)) && e.Actor->GetStaticFlags() & view.StaticFlagsMask) { #if SCENE_RENDERING_USE_PROFILER @@ -54,7 +56,8 @@ void SceneRendering::Draw(RenderContext& renderContext) { for (int32 i = 0; i < Actors.Count(); i++) { - auto& e = Actors[i]; + auto e = Actors[i]; + e.Bounds.Center -= origin; if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds))) { #if SCENE_RENDERING_USE_PROFILER diff --git a/Source/Engine/Physics/Colliders/BoxCollider.cpp b/Source/Engine/Physics/Colliders/BoxCollider.cpp index d3dcc2295..d4f634f64 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.cpp +++ b/Source/Engine/Physics/Colliders/BoxCollider.cpp @@ -129,7 +129,7 @@ void BoxCollider::UpdateBounds() { // Cache bounds OrientedBoundingBox::CreateCentered(_center, _size, _bounds); - _bounds.Transform(_transform.GetWorld()); + _bounds.Transform(_transform); _bounds.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); } diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.cpp b/Source/Engine/Physics/Colliders/CapsuleCollider.cpp index c2a51e800..81f030a9a 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.cpp +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.cpp @@ -99,7 +99,7 @@ void CapsuleCollider::UpdateBounds() // Cache bounds const float radiusTwice = _radius * 2.0f; OrientedBoundingBox::CreateCentered(_center, Vector3(_height + radiusTwice, radiusTwice, radiusTwice), _orientedBox); - _orientedBox.Transform(_transform.GetWorld()); + _orientedBox.Transform(_transform); _orientedBox.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); } diff --git a/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp b/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp index 7556ac3a3..38e0e1a9b 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp @@ -5,7 +5,6 @@ #include "Engine/Core/Math/Math.h" #include "Engine/Level/Actors/BoxBrush.h" #include "Engine/Level/Actors/StaticModel.h" -#include "Engine/ContentImporters/ImportTexture.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Level/Level.h" #include "Engine/Terrain/Terrain.h" @@ -40,10 +39,6 @@ bool cacheStaticGeometryTree(Actor* actor, ShadowsOfMordor::Builder::SceneBuildC entry.AsStaticModel.Actor = staticModel; entry.Scale = Math::Clamp(staticModel->GetScaleInLightmap(), 0.0f, LIGHTMAP_SCALE_MAX); - // Spawn entry for each mesh - Matrix world; - staticModel->GetWorld(&world); - // Use the first LOD const int32 lodIndex = 0; auto& lod = model->LODs[lodIndex]; @@ -61,18 +56,16 @@ bool cacheStaticGeometryTree(Actor* actor, ShadowsOfMordor::Builder::SceneBuildC } else { - LOG(Warning, "Model \'{0}\' mesh index {1} (lod: {2}) has missing lightmap UVs (at actor: {3})", - model->GetPath(), - meshIndex, - lodIndex, - staticModel->GetNamePath()); + LOG(Warning, "Model \'{0}\' mesh index {1} (lod: {2}) has missing lightmap UVs (at actor: {3})", model->GetPath(), meshIndex, lodIndex, staticModel->GetNamePath()); } } } if (useLightmap && anyValid && entry.Scale > ZeroTolerance) { - entry.Box = model->GetBox(world); + Matrix worldMatrix; + staticModel->GetTransform().GetWorld(worldMatrix); + entry.Box = model->GetBox(worldMatrix); results.Add(entry); } else diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index a6d5de4a8..830298f58 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -124,7 +124,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) auto& lod = staticModel->Model->LODs[0]; Matrix worldMatrix; - staticModel->GetWorld(&worldMatrix); + staticModel->GetTransform().GetWorld(worldMatrix); Matrix::Transpose(worldMatrix, shaderData.WorldMatrix); shaderData.LightmapArea = staticModel->Lightmap.UVsArea; diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index b3b63791b..4a6aea948 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -189,7 +189,7 @@ void TerrainChunk::UpdateBounds() { const Vector3 boundsExtent = _patch->_terrain->_boundsExtent; const float size = (float)_patch->_terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX; - Transform terrainTransform = _patch->_terrain->_transform; + const Transform terrainTransform = _patch->_terrain->_transform; Transform localTransform; localTransform.Translation = _patch->_offset + Vector3(_x * size, _yOffset, _z * size); @@ -197,11 +197,8 @@ void TerrainChunk::UpdateBounds() localTransform.Scale = Vector3(size, _yHeight, size); localTransform = terrainTransform.LocalToWorld(localTransform); - Matrix world; - localTransform.GetWorld(world); - OrientedBoundingBox obb(Vector3::Zero, Vector3::One); - obb.Transform(world); + obb.Transform(localTransform); obb.GetBoundingBox(_bounds); _boundsCenter = _bounds.GetCenter();