diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index 72690f8a4..b967cad9b 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -51,74 +51,14 @@ public: { } + static void DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw); + static void DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw); bool Init() override; void Dispose() override; }; ViewportIconsRendererService ViewportIconsRendererServiceInstance; -namespace -{ - void DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw) - { - auto& view = renderContext.View; - const BoundingFrustum frustum = view.Frustum; - auto& icons = scene->GetSceneRendering()->ViewportIcons; - Matrix m1, m2, world; - for (Actor* icon : icons) - { - BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS); - IconTypes iconType; - if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType)) - { - // Create world matrix - Matrix::Scaling(ICON_RADIUS * 2.0f, m2); - Matrix::RotationY(PI, world); - Matrix::Multiply(m2, world, m1); - Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2); - Matrix::Multiply(m1, m2, world); - - // Draw icon - GeometryDrawStateData drawState; - draw.DrawState = &drawState; - draw.Buffer = &InstanceBuffers[static_cast(iconType)]; - draw.World = &world; - draw.Bounds = sphere; - QuadModel->Draw(renderContext, draw); - } - } - } - - void DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw) - { - auto& view = renderContext.View; - const BoundingFrustum frustum = view.Frustum; - Matrix m1, m2, world; - BoundingSphere sphere(actor->GetPosition(), ICON_RADIUS); - IconTypes iconType; - if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType)) - { - // Create world matrix - Matrix::Scaling(ICON_RADIUS * 2.0f, m2); - Matrix::RotationY(PI, world); - Matrix::Multiply(m2, world, m1); - Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2); - Matrix::Multiply(m1, m2, world); - - // Draw icon - GeometryDrawStateData drawState; - draw.DrawState = &drawState; - draw.Buffer = &InstanceBuffers[static_cast(iconType)]; - draw.World = &world; - draw.Bounds = sphere; - QuadModel->Draw(renderContext, draw); - } - - for (auto child : actor->Children) - DrawIcons(renderContext, child, draw); - } -} - void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor) { auto& view = renderContext.View; @@ -137,14 +77,73 @@ void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor if (const auto scene = SceneObject::Cast(actor)) { - ::DrawIcons(renderContext, scene, draw); + ViewportIconsRendererService::DrawIcons(renderContext, scene, draw); } else { - ::DrawIcons(renderContext, actor, draw); + ViewportIconsRendererService::DrawIcons(renderContext, actor, draw); } } +void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw) +{ + auto& view = renderContext.View; + const BoundingFrustum frustum = view.Frustum; + auto& icons = scene->GetSceneRendering()->ViewportIcons; + Matrix m1, m2, world; + for (Actor* icon : icons) + { + BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS); + IconTypes iconType; + if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType)) + { + // Create world matrix + Matrix::Scaling(ICON_RADIUS * 2.0f, m2); + Matrix::RotationY(PI, world); + Matrix::Multiply(m2, world, m1); + Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2); + Matrix::Multiply(m1, m2, world); + + // Draw icon + GeometryDrawStateData drawState; + draw.DrawState = &drawState; + draw.Buffer = &InstanceBuffers[static_cast(iconType)]; + draw.World = &world; + draw.Bounds = sphere; + QuadModel->Draw(renderContext, draw); + } + } +} + +void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw) +{ + auto& view = renderContext.View; + const BoundingFrustum frustum = view.Frustum; + Matrix m1, m2, world; + BoundingSphere sphere(actor->GetPosition(), ICON_RADIUS); + IconTypes iconType; + if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType)) + { + // Create world matrix + Matrix::Scaling(ICON_RADIUS * 2.0f, m2); + Matrix::RotationY(PI, world); + Matrix::Multiply(m2, world, m1); + Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2); + Matrix::Multiply(m1, m2, world); + + // Draw icon + GeometryDrawStateData drawState; + draw.DrawState = &drawState; + draw.Buffer = &InstanceBuffers[static_cast(iconType)]; + draw.World = &world; + draw.Bounds = sphere; + QuadModel->Draw(renderContext, draw); + } + + for (auto child : actor->Children) + DrawIcons(renderContext, child, draw); +} + bool ViewportIconsRendererService::Init() { QuadModel = Content::LoadAsyncInternal(TEXT("Engine/Models/Quad")); diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index d432a5804..c0d2137f1 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -115,16 +115,6 @@ void MaterialInstance::OnBaseParamsChanged() ParamsChanged(); } -void MaterialInstance::OnUnload() -{ - if (_baseMaterial) - { - OnBaseUnset(); - _baseMaterial = nullptr; - } - Params.Dispose(); -} - bool MaterialInstance::IsMaterialInstance() const { return true; @@ -242,7 +232,12 @@ Asset::LoadResult MaterialInstance::load() void MaterialInstance::unload(bool isReloading) { - OnUnload(); + if (_baseMaterial) + { + OnBaseUnset(); + _baseMaterial = nullptr; + } + Params.Dispose(); } AssetChunksFlag MaterialInstance::getChunksToPreload() const diff --git a/Source/Engine/Content/Assets/MaterialInstance.h b/Source/Engine/Content/Assets/MaterialInstance.h index 3cf74db03..dc37133d7 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.h +++ b/Source/Engine/Content/Assets/MaterialInstance.h @@ -49,7 +49,6 @@ private: void OnBaseUnset(); void OnBaseUnloaded(Asset* p); void OnBaseParamsChanged(); - void OnUnload(); public: diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index 5efac818e..c122284ed 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -466,6 +466,17 @@ public: _count++; } + /// + /// Adds the specified item to the collection. + /// + /// The item to add. + void Add(T&& item) + { + EnsureCapacity(_count + 1); + Memory::MoveItems(_allocation.Get() + _count, &item, 1); + _count++; + } + /// /// Adds the specified item to the collection. /// diff --git a/Source/Engine/Core/Collections/ChunkedArray.h b/Source/Engine/Core/Collections/ChunkedArray.h index 0d24e980d..f2c5b4264 100644 --- a/Source/Engine/Core/Collections/ChunkedArray.h +++ b/Source/Engine/Core/Collections/ChunkedArray.h @@ -20,22 +20,15 @@ private: // TODO: don't use Array but small struct and don't InlinedArray or Chunk* but Chunk (less dynamic allocations) typedef Array Chunk; - int32 _count; + int32 _count = 0; Array> _chunks; public: - /// - /// Default constructor - /// ChunkedArray() - : _count(0) { } - /// - /// Destructor - /// ~ChunkedArray() { _chunks.ClearDelete(); @@ -426,13 +419,21 @@ public: /// The new size. void Resize(int32 newSize) { - if (newSize < Count()) + while (newSize < Count()) { - MISSING_CODE("shrinking ChunkedArray on Resize"); + auto& chunk = _chunks.Last(); + int32 itemsLeft = Count() - newSize; + int32 itemsToRemove = Math::Min(chunk->Count(), itemsLeft); + chunk->Resize(chunk->Count() - itemsToRemove); + _count -= itemsToRemove; + if (chunk->Count() == 0) + { + Delete(chunk); + _chunks.RemoveLast(); + } } - else + if (newSize > Count()) { - // Require enough space at once EnsureCapacity(newSize); // Add more items until reach the new size @@ -445,9 +446,9 @@ public: // Insert some items to this chunk if can const int32 spaceLeft = chunk->Capacity() - chunk->Count(); - int32 itemsToInsert = Math::Min(itemsReaming, spaceLeft); - chunk->Resize(chunk->Count() + itemsToInsert); - _count += itemsToInsert; + int32 itemsToAdd = Math::Min(itemsReaming, spaceLeft); + chunk->Resize(chunk->Count() + itemsToAdd); + _count += itemsToAdd; // Update counter itemsReaming = newSize - Count(); @@ -481,29 +482,16 @@ public: public: - /// - /// Gets iterator for beginning of the collection. - /// - /// Iterator for beginning of the collection. Iterator Begin() const { return Iterator(this, 0); } - /// - /// Gets iterator for ending of the collection. - /// - /// Iterator for ending of the collection. Iterator End() const { return Iterator(this, Count()); } - /// - /// Gets iterator for the specified index. - /// - /// The index. - /// Iterator for the specified index. Iterator IteratorAt(int32 index) const { return Iterator(this, index); diff --git a/Source/Engine/Core/Types/CommonValue.h b/Source/Engine/Core/Types/CommonValue.h index 91e468a20..a233610bd 100644 --- a/Source/Engine/Core/Types/CommonValue.h +++ b/Source/Engine/Core/Types/CommonValue.h @@ -425,225 +425,6 @@ public: return *this; } - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const bool value) - { - SetType(CommonType::Bool); - AsBool = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const int32 value) - { - SetType(CommonType::Integer); - AsInteger = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const float value) - { - SetType(CommonType::Float); - AsFloat = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Vector2& value) - { - SetType(CommonType::Vector2); - AsVector2 = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Vector3& value) - { - SetType(CommonType::Vector3); - AsVector3 = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Vector4& value) - { - SetType(CommonType::Vector4); - AsVector4 = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Color& value) - { - SetType(CommonType::Color); - AsColor = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Matrix& value) - { - SetType(CommonType::Matrix); - AsMatrix = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Guid& value) - { - SetType(CommonType::Guid); - AsGuid = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const String& value) - { - Set(value); - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const StringView& value) - { - Set(value); - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const BoundingBox& value) - { - SetType(CommonType::Box); - AsBox = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Quaternion& value) - { - SetType(CommonType::Rotation); - AsRotation = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Transform& value) - { - SetType(CommonType::Transform); - AsTransform = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const BoundingSphere& value) - { - SetType(CommonType::Sphere); - AsSphere = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Rectangle& value) - { - SetType(CommonType::Rectangle); - AsRectangle = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Ray& value) - { - SetType(CommonType::Ray); - AsRay = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(const Char* value) - { - Set(StringView(value)); - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(void* value) - { - SetType(CommonType::Pointer); - AsPointer = value; - return *this; - } - - /// - /// Assignment operator - /// - /// Value - CommonValue& operator=(ScriptingObject* value) - { - SetType(CommonType::Object); - AsObject = value; - if (value) - LinkObject(); - return *this; - } - public: /// diff --git a/Source/Engine/Core/Types/LayersMask.h b/Source/Engine/Core/Types/LayersMask.h index bd8e90335..270e2f83e 100644 --- a/Source/Engine/Core/Types/LayersMask.h +++ b/Source/Engine/Core/Types/LayersMask.h @@ -62,7 +62,7 @@ public: LayersMask operator&(const LayersMask& other) const { - return Mask && other.Mask; + return Mask & other.Mask; } LayersMask operator|(const LayersMask& other) const diff --git a/Source/Engine/Foliage/Config.h b/Source/Engine/Foliage/Config.h index 8f93a2015..cdcc61237 100644 --- a/Source/Engine/Foliage/Config.h +++ b/Source/Engine/Foliage/Config.h @@ -2,7 +2,6 @@ #pragma once -// Forward declarations class Foliage; class FoliageCluster; class FoliageType; @@ -11,6 +10,12 @@ struct FoliageInstance; // Enable/disable foliage editing and changing at runtime. If your game need to use procedural foliage then enable this option. #define FOLIAGE_EDITING (USE_EDITOR) +// Enables using single quad-tree acceleration structure per foliage actor, otherwise will use quad-tree per foliage type to optimize drawing performance at a cost of higher memory usage. +#define FOLIAGE_USE_SINGLE_QUAD_TREE 0 + +// Enables using manual draw calls batching instead of using automated generic solution in RenderList. Boosts performance for large foliage. +#define FOLIAGE_USE_DRAW_CALLS_BATCHING 1 + // Size of the instance allocation chunks (number of instances per allocated page) #define FOLIAGE_INSTANCE_CHUNKS_SIZE (4096*4) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 84b0573d8..c3941edc5 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -7,7 +7,11 @@ #include "Engine/Core/Random.h" #include "Engine/Engine/Engine.h" #include "Engine/Graphics/RenderTask.h" -#include "Engine/Level/Scene/Scene.h" +#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING +#include "Engine/Graphics/RenderTools.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Renderer/RenderList.h" +#endif #include "Engine/Level/SceneQuery.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Serialization/Serialization.h" @@ -17,60 +21,9 @@ Foliage::Foliage(const SpawnParams& params) : Actor(params) { _disableFoliageTypeEvents = false; - Root = nullptr; } -void Foliage::EnsureRoot() -{ - // Skip if root is already here or there is no instances at all - if (Root || Instances.IsEmpty()) - return; - ASSERT(Clusters.IsEmpty()); - - PROFILE_CPU(); - - // Calculate total bounds of valid instances - BoundingBox totalBounds; - { - bool anyValid = false; - // TODO: inline code and use SIMD - BoundingBox box; - for (auto i = Instances.Begin(); i.IsNotEnd(); ++i) - { - if (!FoliageTypes[i->Type].IsReady()) - continue; - - BoundingBox::FromSphere(i->Bounds, box); - ASSERT(!i->Bounds.Center.IsNanOrInfinity()); - - if (anyValid) - { - BoundingBox::Merge(totalBounds, box, totalBounds); - } - else - { - totalBounds = box; - anyValid = true; - } - } - - // Skip if nothing is valid - if (!anyValid) - return; - } - ASSERT(!totalBounds.Minimum.IsNanOrInfinity() && !totalBounds.Maximum.IsNanOrInfinity()); - - // Setup first and topmost cluster - Clusters.Resize(1); - Root = &Clusters[0]; - Root->Init(totalBounds); - - // Cache bounds - _box = Root->Bounds; - BoundingSphere::FromBox(_box, _sphere); -} - -void Foliage::AddToCluster(FoliageCluster* cluster, FoliageInstance& instance) +void Foliage::AddToCluster(ChunkedArray& clusters, FoliageCluster* cluster, FoliageInstance& instance) { ASSERT(instance.Bounds.Radius > ZeroTolerance); ASSERT(cluster->Bounds.Intersects(instance.Bounds)); @@ -100,12 +53,12 @@ void Foliage::AddToCluster(FoliageCluster* cluster, FoliageInstance& instance) else { // Subdivide cluster - const int32 count = Clusters.Count(); - Clusters.Resize(count + 4); - cluster->Children[0] = &Clusters[count + 0]; - cluster->Children[1] = &Clusters[count + 1]; - cluster->Children[2] = &Clusters[count + 2]; - cluster->Children[3] = &Clusters[count + 3]; + const int32 count = clusters.Count(); + clusters.Resize(count + 4); + cluster->Children[0] = &clusters[count + 0]; + cluster->Children[1] = &clusters[count + 1]; + cluster->Children[2] = &clusters[count + 2]; + cluster->Children[3] = &clusters[count + 3]; // Setup children const Vector3 min = cluster->Bounds.Minimum; @@ -119,13 +72,239 @@ void Foliage::AddToCluster(FoliageCluster* cluster, FoliageInstance& instance) // Move instances to a proper cells for (int32 i = 0; i < cluster->Instances.Count(); i++) { - AddToCluster(cluster, *cluster->Instances[i]); + AddToCluster(clusters, cluster, *cluster->Instances[i]); } cluster->Instances.Clear(); - AddToCluster(cluster, instance); + AddToCluster(clusters, cluster, instance); } } +#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING + +void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instance, FoliageType& type, Model* model, const ModelLOD& modelLod, float lodDitherFactor) +{ + for (const auto& mesh : modelLod.Meshes) + { + const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()]; + if (!entry.Visible || !mesh.IsInitialized()) + return; + const MaterialSlot& slot = model->MaterialSlots[mesh.GetMaterialSlotIndex()]; + const auto shadowsMode = static_cast(entry.ShadowsMode & slot.ShadowsMode); + const auto drawModes = static_cast(type._drawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode)); + + // Select material + MaterialBase* material; + if (entry.Material && entry.Material->IsLoaded()) + material = entry.Material; + else if (slot.Material && slot.Material->IsLoaded()) + material = slot.Material; + else + material = GPUDevice::Instance->GetDefaultMaterial(); + if (!material || !material->IsSurface() || drawModes == DrawPass::None) + return; + + // Submit draw call + DrawCall drawCall; + mesh.GetDrawCallGeometry(drawCall); + drawCall.InstanceCount = 1; + drawCall.Material = material; + drawCall.World = instance.World; + drawCall.ObjectPosition = drawCall.World.GetTranslation(); + drawCall.Surface.GeometrySize = mesh.GetBox().GetSize(); + drawCall.Surface.PrevWorld = instance.World; + drawCall.Surface.Lightmap = _staticFlags & StaticFlags::Lightmap ? _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex) : nullptr; + drawCall.Surface.LightmapUVsArea = instance.Lightmap.UVsArea; + drawCall.Surface.Skinning = nullptr; + drawCall.Surface.LODDitherFactor = lodDitherFactor; + drawCall.WorldDeterminantSign = 1; + drawCall.PerInstanceRandom = instance.Random; + renderContext.List->AddDrawCall(drawModes, _staticFlags, drawCall, entry.ReceiveDecals); + } +} + +void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, FoliageType& type) +{ + // Skip clusters that around too far from view + if (Vector3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center) - cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) + return; + + //DebugDraw::DrawBox(cluster->Bounds, Color::Red); + + // Draw visible children + if (cluster->Children[0]) + { + // Don't store instances in non-leaf nodes + ASSERT_LOW_LAYER(cluster->Instances.IsEmpty()); + +#define DRAW_CLUSTER(idx) \ + if (renderContext.View.CullingFrustum.Intersects(cluster->Children[idx]->TotalBounds)) \ + DrawCluster(renderContext, cluster->Children[idx], type) + DRAW_CLUSTER(0); + DRAW_CLUSTER(1); + DRAW_CLUSTER(2); + DRAW_CLUSTER(3); +#undef DRAW_CLUSTER + } + else + { + // Draw visible instances + const auto frame = Engine::FrameCount; + const auto model = type.Model.Get(); + for (int32 i = 0; i < cluster->Instances.Count(); i++) + { + auto& instance = *cluster->Instances[i]; + if (Vector3::Distance(renderContext.View.Position, instance.Bounds.Center) - instance.Bounds.Radius < instance.CullDistance && + renderContext.View.CullingFrustum.Intersects(instance.Bounds)) + { + const auto modelFrame = instance.DrawState.PrevFrame + 1; + + // Select a proper LOD index (model may be culled) + int32 lodIndex = RenderTools::ComputeModelLOD(model, instance.Bounds.Center, instance.Bounds.Radius, renderContext); + if (lodIndex == -1) + { + // Handling model fade-out transition + if (modelFrame == frame && instance.DrawState.PrevLOD != -1) + { + // Check if start transition + if (instance.DrawState.LODTransition == 255) + { + instance.DrawState.LODTransition = 0; + } + + RenderTools::UpdateModelLODTransition(instance.DrawState.LODTransition); + + // Check if end transition + if (instance.DrawState.LODTransition == 255) + { + instance.DrawState.PrevLOD = lodIndex; + } + else + { + const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD); + const float normalizedProgress = static_cast(instance.DrawState.LODTransition) * (1.0f / 255.0f); + DrawInstance(renderContext, instance, type, model, model->LODs[prevLOD], normalizedProgress); + } + } + instance.DrawState.PrevFrame = frame; + continue; + } + lodIndex += renderContext.View.ModelLODBias; + lodIndex = model->ClampLODIndex(lodIndex); + + // Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports) + if (modelFrame == frame) + { + // Check if start transition + if (instance.DrawState.PrevLOD != lodIndex && instance.DrawState.LODTransition == 255) + { + instance.DrawState.LODTransition = 0; + } + + RenderTools::UpdateModelLODTransition(instance.DrawState.LODTransition); + + // Check if end transition + if (instance.DrawState.LODTransition == 255) + { + instance.DrawState.PrevLOD = lodIndex; + } + } + // Check if there was a gap between frames in drawing this model instance + else if (modelFrame < frame || instance.DrawState.PrevLOD == -1) + { + // Reset state + instance.DrawState.PrevLOD = lodIndex; + instance.DrawState.LODTransition = 255; + } + + // Draw + if (instance.DrawState.PrevLOD == lodIndex) + { + DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], 0.0f); + } + else if (instance.DrawState.PrevLOD == -1) + { + const float normalizedProgress = static_cast(instance.DrawState.LODTransition) * (1.0f / 255.0f); + DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], 1.0f - normalizedProgress); + } + else + { + const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD); + const float normalizedProgress = static_cast(instance.DrawState.LODTransition) * (1.0f / 255.0f); + DrawInstance(renderContext, instance, type, model, model->LODs[prevLOD], normalizedProgress); + DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], normalizedProgress - 1.0f); + } + + //DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen); + + instance.DrawState.PrevFrame = frame; + } + } + } +} + +#else + +void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw) +{ + // Skip clusters that around too far from view + if (Vector3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center) - cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) + return; + + //DebugDraw::DrawBox(cluster->Bounds, Color::Red); + + // Draw visible children + if (cluster->Children[0]) + { + // Don't store instances in non-leaf nodes + ASSERT_LOW_LAYER(cluster->Instances.IsEmpty()); + +#define DRAW_CLUSTER(idx) \ + if (renderContext.View.CullingFrustum.Intersects(cluster->Children[idx]->TotalBounds)) \ + DrawCluster(renderContext, cluster->Children[idx], draw) + DRAW_CLUSTER(0); + DRAW_CLUSTER(1); + DRAW_CLUSTER(2); + DRAW_CLUSTER(3); +#undef DRAW_CLUSTER + } + else + { + // Draw visible instances + const auto frame = Engine::FrameCount; + for (int32 i = 0; i < cluster->Instances.Count(); i++) + { + auto& instance = *cluster->Instances[i]; + auto& type = FoliageTypes[instance.Type]; + + // Check if can draw this instance + if (type._canDraw && + Vector3::Distance(renderContext.View.Position, instance.Bounds.Center) - instance.Bounds.Radius < instance.CullDistance && + renderContext.View.CullingFrustum.Intersects(instance.Bounds)) + { + // Disable motion blur + instance.DrawState.PrevWorld = instance.World; + + // Draw model + draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex); + draw.LightmapUVs = &instance.Lightmap.UVsArea; + draw.Buffer = &type.Entries; + draw.World = &instance.World; + draw.DrawState = &instance.DrawState; + draw.Bounds = instance.Bounds; + draw.PerInstanceRandom = instance.Random; + draw.DrawModes = type._drawModes; + type.Model->Draw(renderContext, draw); + + //DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen); + + instance.DrawState.PrevFrame = frame; + } + } + } +} + +#endif + int32 Foliage::GetInstancesCount() const { return Instances.Count(); @@ -363,16 +542,121 @@ void Foliage::RebuildClusters() { PROFILE_CPU(); - // Remove previous clusters data - Root = nullptr; - Clusters.Clear(); + // Faster path if foliage is empty or no types is ready + bool anyTypeValid = false; + for (auto& type : FoliageTypes) + anyTypeValid |= type.IsReady(); + if (!anyTypeValid || Instances.IsEmpty()) + { +#if FOLIAGE_USE_SINGLE_QUAD_TREE + Root = nullptr; + Clusters.Clear(); +#else + for (auto& type : FoliageTypes) + { + type.Root = nullptr; + type.Clusters.Clear(); + } +#endif + _box = BoundingBox(_transform.Translation, _transform.Translation); + _sphere = BoundingSphere(_transform.Translation, 0.0f); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); + return; + } - EnsureRoot(); + // Clear clusters and initialize root + { + PROFILE_CPU_NAMED("Init Root"); + + BoundingBox totalBounds, box; +#if FOLIAGE_USE_SINGLE_QUAD_TREE + { + // Calculate total bounds of all instances + auto i = Instances.Begin(); + for (; i.IsNotEnd(); ++i) + { + if (!FoliageTypes[i->Type].IsReady()) + continue; + BoundingBox::FromSphere(i->Bounds, box); + totalBounds = box; + break; + } + ++i; + // TODO: inline code and use SIMD + for (; i.IsNotEnd(); ++i) + { + if (!FoliageTypes[i->Type].IsReady()) + continue; + BoundingBox::FromSphere(i->Bounds, box); + BoundingBox::Merge(totalBounds, box, totalBounds); + } + } + + // Setup first and topmost cluster + Clusters.Resize(1); + Root = &Clusters[0]; + Root->Init(totalBounds); +#else + bool hasTotalBounds = false; + for (auto& type : FoliageTypes) + { + if (!type.IsReady()) + { + type.Root = nullptr; + type.Clusters.Clear(); + continue; + } + + // Calculate total bounds of all instances of this type + BoundingBox totalBoundsType; + auto i = Instances.Begin(); + for (; i.IsNotEnd(); ++i) + { + if (i->Type == type.Index) + { + BoundingBox::FromSphere(i->Bounds, box); + totalBoundsType = box; + break; + } + } + ++i; + // TODO: inline code and use SIMD + for (; i.IsNotEnd(); ++i) + { + if (i->Type == type.Index) + { + BoundingBox::FromSphere(i->Bounds, box); + BoundingBox::Merge(totalBoundsType, box, totalBoundsType); + } + } + + // Setup first and topmost cluster + type.Clusters.Resize(1); + type.Root = &type.Clusters[0]; + type.Root->Init(totalBoundsType); + if (hasTotalBounds) + { + BoundingBox::Merge(totalBounds, totalBoundsType, totalBounds); + } + else + { + totalBounds = totalBoundsType; + hasTotalBounds = true; + } + } + ASSERT(hasTotalBounds); +#endif + ASSERT(!totalBounds.Minimum.IsNanOrInfinity() && !totalBounds.Maximum.IsNanOrInfinity()); + _box = totalBounds; + BoundingSphere::FromBox(_box, _sphere); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); + } // Insert all instances to the clusters { PROFILE_CPU_NAMED("Create Clusters"); - const float globalDensityScale = GetGlobalDensityScale(); for (auto i = Instances.Begin(); i.IsNotEnd(); ++i) { @@ -382,17 +666,31 @@ void Foliage::RebuildClusters() if (type.IsReady() && instance.Random < densityScale) { - AddToCluster(Root, instance); +#if FOLIAGE_USE_SINGLE_QUAD_TREE + AddToCluster(Clusters, Root, instance); +#else + AddToCluster(type.Clusters, type.Root, instance); +#endif } } } +#if FOLIAGE_USE_SINGLE_QUAD_TREE if (Root) { PROFILE_CPU_NAMED("Update Cache"); - Root->UpdateTotalBoundsAndCullDistance(); } +#else + for (auto& type : FoliageTypes) + { + if (type.Root) + { + PROFILE_CPU_NAMED("Update Cache"); + type.Root->UpdateTotalBoundsAndCullDistance(); + } + } +#endif } void Foliage::UpdateCullDistance() @@ -401,7 +699,6 @@ void Foliage::UpdateCullDistance() { PROFILE_CPU_NAMED("Instances"); - for (auto i = Instances.Begin(); i.IsNotEnd(); ++i) { auto& instance = *i; @@ -410,12 +707,22 @@ void Foliage::UpdateCullDistance() } } +#if FOLIAGE_USE_SINGLE_QUAD_TREE if (Root) { PROFILE_CPU_NAMED("Clusters"); - Root->UpdateCullDistance(); } +#else + for (auto& type : FoliageTypes) + { + if (type.Root) + { + PROFILE_CPU_NAMED("Clusters"); + type.Root->UpdateCullDistance(); + } + } +#endif } static float GlobalDensityScale = 1.0f; @@ -454,117 +761,62 @@ bool Foliage::Intersects(const Ray& ray, float& distance, Vector3& normal, int32 PROFILE_CPU(); instanceIndex = -1; - - if (Root) - { - FoliageInstance* instance; - if (Root->Intersects(this, ray, distance, normal, instance)) - { - int32 j = 0; - for (auto i = Instances.Begin(); i.IsNotEnd(); ++i) - { - if (&*i == instance) - { - instanceIndex = j; - break; - } - j++; - } - - return true; - } - } - - distance = MAX_float; normal = Vector3::Up; - return false; -} + distance = MAX_float; -void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw) -{ - // Skip clusters that around too far from view - if (Vector3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center) - cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) - return; - - //DebugDraw::DrawBox(cluster->Bounds, Color::Red); - - // Draw visible children - if (cluster->Children[0]) + FoliageInstance* instance = nullptr; +#if FOLIAGE_USE_SINGLE_QUAD_TREE + if (Root) + Root->Intersects(this, ray, distance, normal, instance); +#else + float tmpDistance; + Vector3 tmpNormal; + FoliageInstance* tmpInstance; + for (auto& type : FoliageTypes) { -#if BUILD_DEBUG - // Don't store instances in non-leaf nodes - ASSERT(cluster->Instances.IsEmpty()); -#endif - -#define DRAW_CLUSTER(idx) \ - if (renderContext.View.CullingFrustum.Intersects(cluster->Children[idx]->TotalBounds)) \ - DrawCluster(renderContext, cluster->Children[idx], draw) - DRAW_CLUSTER(0); - DRAW_CLUSTER(1); - DRAW_CLUSTER(2); - DRAW_CLUSTER(3); -#undef DRAW_CLUSTER - } - else - { - // Draw visible instances - const auto frame = Engine::FrameCount; - for (int32 i = 0; i < cluster->Instances.Count(); i++) + if (type.Root && type.Root->Intersects(this, ray, tmpDistance, tmpNormal, tmpInstance) && tmpDistance < distance) { - auto& instance = *cluster->Instances[i]; - auto& type = FoliageTypes[instance.Type]; - - // Check if can draw this instance - if (type._canDraw && - Vector3::Distance(renderContext.View.Position, instance.Bounds.Center) - instance.Bounds.Radius < instance.CullDistance && - renderContext.View.CullingFrustum.Intersects(instance.Bounds)) - { - // Disable motion blur - instance.DrawState.PrevWorld = instance.World; - - // Draw model - draw.Lightmap = GetScene()->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex); - draw.LightmapUVs = &instance.Lightmap.UVsArea; - draw.Buffer = &type.Entries; - draw.World = &instance.World; - draw.DrawState = &instance.DrawState; - draw.Bounds = instance.Bounds; - draw.PerInstanceRandom = instance.Random; - draw.DrawModes = type._drawModes; - type.Model->Draw(renderContext, draw); - - //DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen); - - instance.DrawState.PrevFrame = frame; - } + distance = tmpDistance; + normal = tmpNormal; + instance = tmpInstance; } } +#endif + if (instance != nullptr) + { + int32 j = 0; + for (auto i = Instances.Begin(); i.IsNotEnd(); ++i) + { + if (&*i == instance) + { + instanceIndex = j; + return true; + } + j++; + } + } + return false; } void Foliage::Draw(RenderContext& renderContext) { - // Skip if no instances spawned - if (Instances.IsEmpty() || !Root) + if (Instances.IsEmpty()) return; auto& view = renderContext.View; PROFILE_CPU(); // Cache data per foliage instance type - for (int32 i = 0; i < FoliageTypes.Count(); i++) + for (auto& type : FoliageTypes) { - auto& type = FoliageTypes[i]; - const auto drawModes = static_cast(type.DrawModes & view.Pass & (int32)view.GetShadowsDrawPassMask(type.ShadowsMode)); type._canDraw = type.IsReady() && drawModes != DrawPass::None; type._drawModes = drawModes; - if (type._canDraw) { for (int32 j = 0; j < type.Entries.Count(); j++) { auto& e = type.Entries[j]; - e.ReceiveDecals = type.ReceiveDecals != 0; e.ShadowsMode = type.ShadowsMode; } @@ -572,13 +824,24 @@ void Foliage::Draw(RenderContext& renderContext) } // Draw visible clusters +#if FOLIAGE_USE_SINGLE_QUAD_TREE Mesh::DrawInfo draw; draw.Flags = GetStaticFlags(); draw.DrawModes = (DrawPass)(DrawPass::Default & view.Pass); draw.LODBias = 0; draw.ForcedLOD = -1; draw.VertexColors = nullptr; - DrawCluster(renderContext, Root, draw); + if (Root) + DrawCluster(renderContext, Root, draw); +#else + for (auto& type : FoliageTypes) + { + if (type.Root && type._canDraw && type.Model->CanBeRendered()) + { + DrawCluster(renderContext, type.Root, type); + } + } +#endif } void Foliage::DrawGeneric(RenderContext& renderContext) @@ -640,10 +903,10 @@ void Foliage::Serialize(SerializeStream& stream, const void* otherObj) stream.JKEY("Foliage"); stream.StartArray(); - for (int32 i = 0; i < FoliageTypes.Count(); i++) + for (auto& type : FoliageTypes) { stream.StartObject(); - FoliageTypes[i].Serialize(stream, nullptr); + type.Serialize(stream, nullptr); stream.EndObject(); } stream.EndArray(); @@ -676,9 +939,11 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie PROFILE_CPU(); // Clear +#if FOLIAGE_USE_SINGLE_QUAD_TREE Root = nullptr; - Instances.Release(); Clusters.Release(); +#endif + Instances.Release(); FoliageTypes.Resize(0, false); // Deserialize foliage types @@ -785,9 +1050,15 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie } } +void Foliage::OnLayerChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); +} + void Foliage::OnEnable() { - GetScene()->Rendering.AddGeometry(this); + _sceneRenderingKey = GetSceneRendering()->AddGeometry(this); // Base Actor::OnEnable(); @@ -795,7 +1066,7 @@ void Foliage::OnEnable() void Foliage::OnDisable() { - GetScene()->Rendering.RemoveGeometry(this); + GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey); // Base Actor::OnDisable(); diff --git a/Source/Engine/Foliage/Foliage.h b/Source/Engine/Foliage/Foliage.h index 3b22e1c67..505b7d405 100644 --- a/Source/Engine/Foliage/Foliage.h +++ b/Source/Engine/Foliage/Foliage.h @@ -7,7 +7,6 @@ #include "FoliageCluster.h" #include "FoliageType.h" #include "Engine/Level/Actor.h" -#include "Engine/Core/Collections/ChunkedArray.h" /// /// Represents a foliage actor that contains a set of instanced meshes. @@ -19,23 +18,26 @@ DECLARE_SCENE_OBJECT(Foliage); private: bool _disableFoliageTypeEvents; + int32 _sceneRenderingKey = -1; public: - /// - /// The root cluster. Contains all the instances and it's the starting point of the quad-tree hierarchy. Null if no foliage added. It's read-only. - /// - FoliageCluster* Root; - /// /// The allocated foliage instances. It's read-only. /// ChunkedArray Instances; +#if FOLIAGE_USE_SINGLE_QUAD_TREE + /// + /// The root cluster. Contains all the instances and it's the starting point of the quad-tree hierarchy. Null if no foliage added. It's read-only. + /// + FoliageCluster* Root = nullptr; + /// /// The allocated foliage clusters. It's read-only. /// ChunkedArray Clusters; +#endif /// /// The foliage instances types used by the current foliage actor. It's read-only. @@ -140,36 +142,22 @@ public: /// /// Gets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices. /// - /// The value. API_PROPERTY() static float GetGlobalDensityScale(); /// /// Sets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices. /// - /// The value. API_PROPERTY() static void SetGlobalDensityScale(float value); private: - /// - /// Ensures that the root node of the foliage was added. - /// - void EnsureRoot(); - - /// - /// Adds the given foliage instance to the cluster. - /// - /// The root cluster. - /// The instance. - void AddToCluster(FoliageCluster* cluster, FoliageInstance& instance); - - /// - /// Draws the cluster. - /// - /// The rendering context. - /// The cluster. - /// The draw data. + void AddToCluster(ChunkedArray& clusters, FoliageCluster* cluster, FoliageInstance& instance); +#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING + void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, FoliageType& type, Model* model, const ModelLOD& modelLod, float lodDitherFactor); + void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, FoliageType& type); +#else void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw); +#endif public: @@ -191,6 +179,7 @@ public: bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void OnLayerChanged() override; protected: diff --git a/Source/Engine/Foliage/FoliageInstance.h b/Source/Engine/Foliage/FoliageInstance.h index a11a6212d..a4dd2b25b 100644 --- a/Source/Engine/Foliage/FoliageInstance.h +++ b/Source/Engine/Foliage/FoliageInstance.h @@ -64,7 +64,6 @@ public: /// /// Determines whether this foliage instance has valid lightmap data. /// - /// true if this foliage instance has valid lightmap data; otherwise, false. FORCE_INLINE bool HasLightmap() const { return Lightmap.TextureIndex != INVALID_INDEX; diff --git a/Source/Engine/Foliage/FoliageType.h b/Source/Engine/Foliage/FoliageType.h index 6a84abb1a..537c5be01 100644 --- a/Source/Engine/Foliage/FoliageType.h +++ b/Source/Engine/Foliage/FoliageType.h @@ -3,6 +3,7 @@ #pragma once #include "Config.h" +#include "Engine/Core/Collections/ChunkedArray.h" #include "Engine/Content/Assets/Model.h" #include "Engine/Serialization/ISerializable.h" @@ -86,6 +87,18 @@ public: /// The shared model instance entries. /// ModelInstanceEntries Entries; + +#if !FOLIAGE_USE_SINGLE_QUAD_TREE + /// + /// The root cluster. Contains all the instances and it's the starting point of the quad-tree hierarchy. Null if no foliage added. It's read-only. + /// + FoliageCluster* Root = nullptr; + + /// + /// The allocated foliage clusters. It's read-only. + /// + ChunkedArray Clusters; +#endif public: @@ -206,7 +219,6 @@ public: /// /// Determines whether this instance is ready (model is loaded). /// - /// true if foliage type model is loaded and instance type is ready; otherwise, false. FORCE_INLINE bool IsReady() const { return _isReady != 0; @@ -215,7 +227,6 @@ public: /// /// Gets the random scale for the foliage instance of this type. /// - /// The scale vector. Vector3 GetRandomScale() const; private: diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 45bc701c6..d4065c0b3 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -439,6 +439,8 @@ void AnimatedModel::UpdateBounds() BoundingBox::Transform(_boxLocal, _world, _box); BoundingSphere::FromBox(_box, _sphere); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } void AnimatedModel::UpdateSockets() @@ -758,4 +760,6 @@ void AnimatedModel::OnTransformChanged() _transform.GetWorld(_world); BoundingBox::Transform(_boxLocal, _world, _box); BoundingSphere::FromBox(_box, _sphere); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } diff --git a/Source/Engine/Level/Actors/Decal.cpp b/Source/Engine/Level/Actors/Decal.cpp index c7e8475b3..994b0fb72 100644 --- a/Source/Engine/Level/Actors/Decal.cpp +++ b/Source/Engine/Level/Actors/Decal.cpp @@ -60,6 +60,12 @@ void Decal::OnDebugDrawSelected() #endif +void Decal::OnLayerChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey); +} + void Decal::Draw(RenderContext& renderContext) { if ((renderContext.View.Flags & ViewFlags::Decals) != 0 && @@ -109,7 +115,7 @@ bool Decal::IntersectsItself(const Ray& ray, float& distance, Vector3& normal) void Decal::OnEnable() { - GetSceneRendering()->AddCommon(this); + _sceneRenderingKey = GetSceneRendering()->AddCommon(this); #if USE_EDITOR GetSceneRendering()->AddViewportIcon(this); #endif @@ -123,7 +129,7 @@ void Decal::OnDisable() #if USE_EDITOR GetSceneRendering()->RemoveViewportIcon(this); #endif - GetSceneRendering()->RemoveCommon(this); + GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey); // Base Actor::OnDisable(); @@ -142,4 +148,7 @@ void Decal::OnTransformChanged() _bounds.Transformation = _world; _bounds.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); + + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey); } diff --git a/Source/Engine/Level/Actors/Decal.h b/Source/Engine/Level/Actors/Decal.h index 78a92bfd6..7bff6dbff 100644 --- a/Source/Engine/Level/Actors/Decal.h +++ b/Source/Engine/Level/Actors/Decal.h @@ -18,6 +18,7 @@ private: Vector3 _size; OrientedBoundingBox _bounds; Matrix _world; + int32 _sceneRenderingKey = -1; public: @@ -77,6 +78,7 @@ public: #if USE_EDITOR void OnDebugDrawSelected() override; #endif + void OnLayerChanged() override; void Draw(RenderContext& renderContext) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.cpp b/Source/Engine/Level/Actors/EnvironmentProbe.cpp index d5939d722..0b9348129 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.cpp +++ b/Source/Engine/Level/Actors/EnvironmentProbe.cpp @@ -20,7 +20,8 @@ EnvironmentProbe::EnvironmentProbe(const SpawnParams& params) , _isUsingCustomProbe(false) , _probe(nullptr) { - UpdateBounds(); + _sphere = BoundingSphere(Vector3::Zero, _radius); + BoundingBox::FromSphere(_sphere, _box); } float EnvironmentProbe::GetRadius() const @@ -125,6 +126,8 @@ void EnvironmentProbe::UpdateBounds() { _sphere = BoundingSphere(GetPosition(), GetScaledRadius()); BoundingBox::FromSphere(_sphere, _box); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey); } void EnvironmentProbe::Draw(RenderContext& renderContext) @@ -150,6 +153,12 @@ void EnvironmentProbe::OnDebugDrawSelected() #endif +void EnvironmentProbe::OnLayerChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey); +} + void EnvironmentProbe::Serialize(SerializeStream& stream, const void* otherObj) { // Base @@ -190,7 +199,7 @@ bool EnvironmentProbe::IntersectsItself(const Ray& ray, float& distance, Vector3 void EnvironmentProbe::OnEnable() { - GetSceneRendering()->AddCommon(this); + _sceneRenderingKey = GetSceneRendering()->AddCommon(this); #if USE_EDITOR GetSceneRendering()->AddViewportIcon(this); #endif @@ -204,7 +213,7 @@ void EnvironmentProbe::OnDisable() #if USE_EDITOR GetSceneRendering()->RemoveViewportIcon(this); #endif - GetSceneRendering()->RemoveCommon(this); + GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey); // Base Actor::OnDisable(); diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.h b/Source/Engine/Level/Actors/EnvironmentProbe.h index 58fdcf73b..a8fcc8e08 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.h +++ b/Source/Engine/Level/Actors/EnvironmentProbe.h @@ -16,6 +16,7 @@ private: float _radius; bool _isUsingCustomProbe; + int32 _sceneRenderingKey = -1; AssetReference _probe; public: @@ -138,6 +139,7 @@ public: #if USE_EDITOR void OnDebugDrawSelected() override; #endif + void OnLayerChanged() override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; bool HasContentLoaded() const override; diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.cpp b/Source/Engine/Level/Actors/ModelInstanceActor.cpp index 989eceb9f..42a81244c 100644 --- a/Source/Engine/Level/Actors/ModelInstanceActor.cpp +++ b/Source/Engine/Level/Actors/ModelInstanceActor.cpp @@ -31,9 +31,15 @@ MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 return result; } +void ModelInstanceActor::OnLayerChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); +} + void ModelInstanceActor::OnEnable() { - GetSceneRendering()->AddGeometry(this); + _sceneRenderingKey = GetSceneRendering()->AddGeometry(this); // Base Actor::OnEnable(); @@ -41,7 +47,7 @@ void ModelInstanceActor::OnEnable() void ModelInstanceActor::OnDisable() { - GetSceneRendering()->RemoveGeometry(this); + GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey); // Base Actor::OnDisable(); diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.h b/Source/Engine/Level/Actors/ModelInstanceActor.h index 7b2a08fd9..2bf55fcc9 100644 --- a/Source/Engine/Level/Actors/ModelInstanceActor.h +++ b/Source/Engine/Level/Actors/ModelInstanceActor.h @@ -12,6 +12,10 @@ API_CLASS(Abstract) class FLAXENGINE_API ModelInstanceActor : public Actor { DECLARE_SCENE_OBJECT_ABSTRACT(ModelInstanceActor); +protected: + + int32 _sceneRenderingKey = -1; + public: /// @@ -79,6 +83,11 @@ public: return false; } +public: + + // [Actor] + void OnLayerChanged() override; + protected: // [Actor] diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp index 9341002c1..a83f443cd 100644 --- a/Source/Engine/Level/Actors/PointLight.cpp +++ b/Source/Engine/Level/Actors/PointLight.cpp @@ -15,7 +15,9 @@ PointLight::PointLight(const SpawnParams& params) ShadowsDistance = 2000.0f; ShadowsFadeDistance = 100.0f; ShadowsDepthBias = 0.5f; - UpdateBounds(); + _direction = Vector3::Forward; + _sphere = BoundingSphere(Vector3::Zero, _radius); + BoundingBox::FromSphere(_sphere, _box); } float PointLight::ComputeBrightness() const @@ -62,11 +64,14 @@ void PointLight::UpdateBounds() // Cache bounding box _sphere = BoundingSphere(GetPosition(), GetScaledRadius()); BoundingBox::FromSphere(_sphere, _box); + + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey); } void PointLight::OnEnable() { - GetSceneRendering()->AddCommon(this); + _sceneRenderingKey = GetSceneRendering()->AddCommon(this); #if USE_EDITOR GetSceneRendering()->AddViewportIcon(this); #endif @@ -80,7 +85,7 @@ void PointLight::OnDisable() #if USE_EDITOR GetSceneRendering()->RemoveViewportIcon(this); #endif - GetSceneRendering()->RemoveCommon(this); + GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey); // Base LightWithShadow::OnDisable(); @@ -157,6 +162,12 @@ void PointLight::OnDebugDrawSelected() #endif +void PointLight::OnLayerChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey); +} + void PointLight::Serialize(SerializeStream& stream, const void* otherObj) { // Base diff --git a/Source/Engine/Level/Actors/PointLight.h b/Source/Engine/Level/Actors/PointLight.h index defa6d749..53c4fd309 100644 --- a/Source/Engine/Level/Actors/PointLight.h +++ b/Source/Engine/Level/Actors/PointLight.h @@ -16,6 +16,7 @@ private: Vector3 _direction; float _radius; + int32 _sceneRenderingKey = -1; public: @@ -101,6 +102,7 @@ public: void OnDebugDraw() override; void OnDebugDrawSelected() override; #endif + void OnLayerChanged() override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override; diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp index ec25ffb5b..c4c5c8c86 100644 --- a/Source/Engine/Level/Actors/SpotLight.cpp +++ b/Source/Engine/Level/Actors/SpotLight.cpp @@ -18,7 +18,14 @@ SpotLight::SpotLight(const SpawnParams& params) ShadowsDistance = 2000.0f; ShadowsFadeDistance = 100.0f; ShadowsDepthBias = 0.5f; - UpdateBounds(); + + _direction = Vector3::Forward; + _cosOuterCone = Math::Cos(_outerConeAngle * DegreesToRadians); + _cosInnerCone = Math::Cos(_innerConeAngle * DegreesToRadians); + _invCosConeDifference = 1.0f / (_cosInnerCone - _cosOuterCone); + const float boundsRadius = Math::Sqrt(1.25f * _radius * _radius - _radius * _radius * _cosOuterCone); + _sphere = BoundingSphere(GetPosition() + 0.5f * GetDirection() * _radius, boundsRadius); + BoundingBox::FromSphere(_sphere, _box); } float SpotLight::ComputeBrightness() const @@ -105,11 +112,14 @@ void SpotLight::UpdateBounds() const float boundsRadius = Math::Sqrt(1.25f * radius * radius - radius * radius * _cosOuterCone); _sphere = BoundingSphere(GetPosition() + 0.5f * GetDirection() * radius, boundsRadius); BoundingBox::FromSphere(_sphere, _box); + + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey); } void SpotLight::OnEnable() { - GetSceneRendering()->AddCommon(this); + _sceneRenderingKey = GetSceneRendering()->AddCommon(this); #if USE_EDITOR GetSceneRendering()->AddViewportIcon(this); #endif @@ -123,7 +133,7 @@ void SpotLight::OnDisable() #if USE_EDITOR GetSceneRendering()->RemoveViewportIcon(this); #endif - GetSceneRendering()->RemoveCommon(this); + GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey); // Base LightWithShadow::OnDisable(); diff --git a/Source/Engine/Level/Actors/SpotLight.h b/Source/Engine/Level/Actors/SpotLight.h index 19f6ab1a3..3d8974669 100644 --- a/Source/Engine/Level/Actors/SpotLight.h +++ b/Source/Engine/Level/Actors/SpotLight.h @@ -21,6 +21,7 @@ private: float _cosOuterCone; float _cosInnerCone; float _invCosConeDifference; + int32 _sceneRenderingKey = -1; public: diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index 0005e74f9..072f795b2 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -193,6 +193,8 @@ void StaticModel::UpdateBounds() _box = BoundingBox(_transform.Translation); } BoundingSphere::FromBox(_box, _sphere); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } bool StaticModel::HasContentLoaded() const @@ -245,7 +247,7 @@ void StaticModel::Draw(RenderContext& renderContext) draw.DrawState = &_drawState; draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); draw.LightmapUVs = &Lightmap.UVsArea; - draw.Flags = GetStaticFlags(); + draw.Flags = _staticFlags; draw.DrawModes = drawModes; draw.Bounds = _sphere; draw.PerInstanceRandom = GetPerInstanceRandom(); diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index efc73dc63..2f77600b8 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -588,9 +588,9 @@ public: { LOG(Info, "Prepare scene objects"); SceneBeginData beginData; - for (int32 i = 0; i < Level::Scenes.Count(); i++) + for (auto scene : Level::Scenes) { - Level::Scenes[i]->BeginPlay(&beginData); + scene->BeginPlay(&beginData); } beginData.OnDone(); } diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp index 9304b679a..b9b5444ab 100644 --- a/Source/Engine/Level/Scene/Scene.cpp +++ b/Source/Engine/Level/Scene/Scene.cpp @@ -30,13 +30,37 @@ bool SceneAsset::IsInternalType() const return true; } +BoundingBox SceneNavigation::GetNavigationBounds() +{ + if (Volumes.IsEmpty()) + return BoundingBox::Empty; + PROFILE_CPU_NAMED("GetNavigationBounds"); + auto box = Volumes[0]->GetBox(); + for (int32 i = 1; i < Volumes.Count(); i++) + BoundingBox::Merge(box, Volumes[i]->GetBox(), box); + return box; +} + +NavMeshBoundsVolume* SceneNavigation::FindNavigationBoundsOverlap(const BoundingBox& bounds) +{ + NavMeshBoundsVolume* result = nullptr; + for (int32 i = 0; i < Volumes.Count(); i++) + { + if (Volumes[i]->GetBox().Intersects(bounds)) + { + result = Volumes[i]; + break; + } + } + return result; +} + #define CSG_COLLIDER_NAME TEXT("CSG.Collider") #define CSG_MODEL_NAME TEXT("CSG.Model") Scene::Scene(const SpawnParams& params) : Actor(params) , Rendering(this) - , Ticking(this) , LightmapsData(this) , CSGData(this) { @@ -65,31 +89,6 @@ void Scene::SetLightmapSettings(const LightmapSettings& value) Info.LightmapSettings = value; } -BoundingBox Scene::GetNavigationBounds() -{ - if (NavigationVolumes.IsEmpty()) - return BoundingBox::Empty; - PROFILE_CPU_NAMED("GetNavigationBounds"); - auto box = NavigationVolumes[0]->GetBox(); - for (int32 i = 1; i < NavigationVolumes.Count(); i++) - BoundingBox::Merge(box, NavigationVolumes[i]->GetBox(), box); - return box; -} - -NavMeshBoundsVolume* Scene::FindNavigationBoundsOverlap(const BoundingBox& bounds) -{ - NavMeshBoundsVolume* result = nullptr; - for (int32 i = 0; i < NavigationVolumes.Count(); i++) - { - if (NavigationVolumes[i]->GetBox().Intersects(bounds)) - { - result = NavigationVolumes[i]; - break; - } - } - return result; -} - void Scene::ClearLightmaps() { LightmapsData.ClearLightmaps(); @@ -283,7 +282,7 @@ void Scene::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) CSGData.DeserializeIfExists(stream, "CSG", modifier); // [Deprecated on 13.01.2021, expires on 13.01.2023] - if (modifier->EngineBuild <= 6215 && NavigationMeshes.IsEmpty()) + if (modifier->EngineBuild <= 6215 && Navigation.Meshes.IsEmpty()) { const auto e = SERIALIZE_FIND_MEMBER(stream, "NavMesh"); if (e != stream.MemberEnd()) diff --git a/Source/Engine/Level/Scene/Scene.h b/Source/Engine/Level/Scene/Scene.h index 305a31ca6..474e0907f 100644 --- a/Source/Engine/Level/Scene/Scene.h +++ b/Source/Engine/Level/Scene/Scene.h @@ -8,22 +8,18 @@ #include "SceneCSGData.h" #include "SceneRendering.h" #include "SceneTicking.h" +#include "SceneNavigation.h" class MeshCollider; -class Level; -class ReloadScriptsAction; -class NavMeshBoundsVolume; -class NavMesh; /// /// The scene root object that contains a hierarchy of actors. /// API_CLASS() class FLAXENGINE_API Scene : public Actor { + friend class Level; + friend class ReloadScriptsAction; DECLARE_SCENE_OBJECT(Scene); - friend Level; - friend ReloadScriptsAction; -public: /// /// Finalizes an instance of the class. @@ -47,8 +43,6 @@ public: /// DateTime SaveTime; -public: - /// /// The scene rendering manager. /// @@ -59,6 +53,11 @@ public: /// SceneTicking Ticking; + /// + /// The navigation data. + /// + SceneNavigation Navigation; + /// /// The static light manager for this scene. /// @@ -80,31 +79,6 @@ public: /// API_PROPERTY() void SetLightmapSettings(const LightmapSettings& value); -public: - - /// - /// The list of registered navigation bounds volumes (in the scene). - /// - Array NavigationVolumes; - - /// - /// The list of registered navigation meshes (in the scene). - /// - Array NavigationMeshes; - - /// - /// Gets the total navigation volumes bounds. - /// - /// The navmesh bounds. - BoundingBox GetNavigationBounds(); - - /// - /// Finds the navigation volume bounds that have intersection with the given world-space bounding box. - /// - /// The bounds. - /// The intersecting volume or null if none found. - NavMeshBoundsVolume* FindNavigationBoundsOverlap(const BoundingBox& bounds); - public: /// @@ -119,8 +93,6 @@ public: /// The timeout to wait before building CSG (in milliseconds). API_FUNCTION() void BuildCSG(float timeoutMs = 50); -public: - #if USE_EDITOR /// diff --git a/Source/Engine/Level/Scene/SceneCSGData.h b/Source/Engine/Level/Scene/SceneCSGData.h index 0f85e75af..373ebd06c 100644 --- a/Source/Engine/Level/Scene/SceneCSGData.h +++ b/Source/Engine/Level/Scene/SceneCSGData.h @@ -2,13 +2,13 @@ #pragma once -#include "Engine/Content/AssetReference.h" -#include "Engine/Content/Assets/RawDataAsset.h" -#include "Engine/Content/Assets/Model.h" -#include "Engine/Serialization/ISerializable.h" #include "Engine/Core/Math/Triangle.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Physics/CollisionData.h" +#include "Engine/Serialization/ISerializable.h" +#include "Engine/Content/AssetReference.h" +#include "Engine/Content/Assets/RawDataAsset.h" +#include "Engine/Content/Assets/Model.h" class Scene; @@ -74,7 +74,6 @@ namespace CSG /// /// Determines whether this container has CSG data linked. /// - /// true if this CSG data container is valid; otherwise, false. bool HasData() const; public: diff --git a/Source/Engine/Level/Scene/SceneNavigation.h b/Source/Engine/Level/Scene/SceneNavigation.h new file mode 100644 index 000000000..31ccb63b9 --- /dev/null +++ b/Source/Engine/Level/Scene/SceneNavigation.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Collections/Array.h" + +class NavMesh; +class NavMeshBoundsVolume; + +/// +/// Scene navigation subsystem. +/// +class FLAXENGINE_API SceneNavigation +{ +public: + + /// + /// The list of registered navigation bounds volumes (on the scene). + /// + Array Volumes; + + /// + /// The list of registered navigation meshes (on the scene). + /// + Array Meshes; + + /// + /// Gets the total navigation volumes bounds. + /// + BoundingBox GetNavigationBounds(); + + /// + /// Finds the navigation volume bounds that have intersection with the given world-space bounding box. + /// + /// The bounds. + /// The intersecting volume or null if none found. + NavMeshBoundsVolume* FindNavigationBoundsOverlap(const BoundingBox& bounds); +}; diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp index ce93b3e1c..b99b96af6 100644 --- a/Source/Engine/Level/Scene/SceneRendering.cpp +++ b/Source/Engine/Level/Scene/SceneRendering.cpp @@ -4,242 +4,315 @@ #include "Scene.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderView.h" -#include "Engine/Level/Actors/PostFxVolume.h" +#define SCENE_RENDERING_USE_PROFILER 0 #define SCENE_RENDERING_USE_SIMD 0 +#if SCENE_RENDERING_USE_PROFILER +#include "Engine/Profiler/ProfilerCPU.h" +#endif + #if SCENE_RENDERING_USE_SIMD #include "Engine/Core/SIMD.h" ALIGN_BEGIN(16) struct CullDataSIMD { - float xs[8]; - float ys[8]; - float zs[8]; - float ds[8]; + float xs[8]; + float ys[8]; + float zs[8]; + float ds[8]; } ALIGN_END(16); #endif +int32 SceneRendering::DrawEntries::Add(Actor* obj) +{ + int32 key = 0; + for (; key < List.Count(); key++) + { + if (List[key].Actor == nullptr) + break; + } + if (key == List.Count()) + List.AddOne(); + auto& e = List[key]; + e.Actor = obj; + e.LayerMask = obj->GetLayerMask(); + e.Bounds = obj->GetSphere(); + return key; +} + +void SceneRendering::DrawEntries::Update(Actor* obj, int32 key) +{ + auto& e = List[key]; + ASSERT_LOW_LAYER(obj == e.Actor); + e.LayerMask = obj->GetLayerMask(); + e.Bounds = obj->GetSphere(); +} + +void SceneRendering::DrawEntries::Remove(Actor* obj, int32 key) +{ + if (List.IsEmpty()) + return; + auto& e = List[key]; + ASSERT_LOW_LAYER(obj == e.Actor); + e.Actor = nullptr; + e.LayerMask = 0; +} + +void SceneRendering::DrawEntries::Clear() +{ + List.Clear(); +} + +void SceneRendering::DrawEntries::CullAndDraw(RenderContext& renderContext) +{ + auto& view = renderContext.View; + const BoundingFrustum frustum = view.CullingFrustum; +#if SCENE_RENDERING_USE_SIMD + CullDataSIMD cullData; + { + // Near + auto plane = view.Frustum.GetNear(); + cullData.xs[0] = plane.Normal.X; + cullData.ys[0] = plane.Normal.Y; + cullData.zs[0] = plane.Normal.Z; + cullData.ds[0] = plane.D; + + // Far + plane = view.Frustum.GetFar(); + cullData.xs[1] = plane.Normal.X; + cullData.ys[1] = plane.Normal.Y; + cullData.zs[1] = plane.Normal.Z; + cullData.ds[1] = plane.D; + + // Left + plane = view.Frustum.GetLeft(); + cullData.xs[2] = plane.Normal.X; + cullData.ys[2] = plane.Normal.Y; + cullData.zs[2] = plane.Normal.Z; + cullData.ds[2] = plane.D; + + // Right + plane = view.Frustum.GetRight(); + cullData.xs[3] = plane.Normal.X; + cullData.ys[3] = plane.Normal.Y; + cullData.zs[3] = plane.Normal.Z; + cullData.ds[3] = plane.D; + + // Top + plane = view.Frustum.GetTop(); + cullData.xs[4] = plane.Normal.X; + cullData.ys[4] = plane.Normal.Y; + cullData.zs[4] = plane.Normal.Z; + cullData.ds[4] = plane.D; + + // Bottom + plane = view.Frustum.GetBottom(); + cullData.xs[5] = plane.Normal.X; + cullData.ys[5] = plane.Normal.Y; + cullData.zs[5] = plane.Normal.Z; + cullData.ds[5] = plane.D; + + // Extra 0 + cullData.xs[6] = 0; + cullData.ys[6] = 0; + cullData.zs[6] = 0; + cullData.ds[6] = 0; + + // Extra 1 + cullData.xs[7] = 0; + cullData.ys[7] = 0; + cullData.zs[7] = 0; + cullData.ds[7] = 0; + } + + SimdVector4 px = SIMD::Load(cullData.xs); + SimdVector4 py = SIMD::Load(cullData.ys); + SimdVector4 pz = SIMD::Load(cullData.zs); + SimdVector4 pd = SIMD::Load(cullData.ds); + SimdVector4 px2 = SIMD::Load(&cullData.xs[4]); + SimdVector4 py2 = SIMD::Load(&cullData.ys[4]); + SimdVector4 pz2 = SIMD::Load(&cullData.zs[4]); + SimdVector4 pd2 = SIMD::Load(&cullData.ds[4]); + + for (int32 i = 0; i < List.Count(); i++) + { + auto e = List[i]; + + SimdVector4 cx = SIMD::Splat(e.Bounds.Center.X); + SimdVector4 cy = SIMD::Splat(e.Bounds.Center.Y); + SimdVector4 cz = SIMD::Splat(e.Bounds.Center.Z); + SimdVector4 r = SIMD::Splat(-e.Bounds.Radius); + + SimdVector4 t = SIMD::Mul(cx, px); + t = SIMD::Add(t, SIMD::Mul(cy, py)); + t = SIMD::Add(t, SIMD::Mul(cz, pz)); + t = SIMD::Add(t, pd); + t = SIMD::Sub(t, r); + if (SIMD::MoveMask(t)) + continue; + + t = SIMD::Mul(cx, px2); + t = SIMD::Add(t, SIMD::Mul(cy, py2)); + t = SIMD::Add(t, SIMD::Mul(cz, pz2)); + t = SIMD::Add(t, pd2); + t = SIMD::Sub(t, r); + if (SIMD::MoveMask(t)) + continue; + + if (view.RenderLayersMask.Mask & e.LayerMask) + { +#if SCENE_RENDERING_USE_PROFILER + PROFILE_CPU(); + ___tracy_scoped_zone.Name(*e.Actor->GetName(), e.Actor->GetName().Length()); +#endif + e.Actor->Draw(renderContext); + } + } +#else + for (int32 i = 0; i < List.Count(); i++) + { + auto e = List[i]; + if (view.RenderLayersMask.Mask & e.LayerMask && frustum.Intersects(e.Bounds)) + { +#if SCENE_RENDERING_USE_PROFILER + PROFILE_CPU(); + ___tracy_scoped_zone.Name(*e.Actor->GetName(), e.Actor->GetName().Length()); +#endif + e.Actor->Draw(renderContext); + } + } +#endif +} + +void SceneRendering::DrawEntries::CullAndDrawOffline(RenderContext& renderContext) +{ + auto& view = renderContext.View; + const BoundingFrustum frustum = view.CullingFrustum; +#if SCENE_RENDERING_USE_SIMD + CullDataSIMD cullData; + { + // Near + auto plane = view.Frustum.GetNear(); + cullData.xs[0] = plane.Normal.X; + cullData.ys[0] = plane.Normal.Y; + cullData.zs[0] = plane.Normal.Z; + cullData.ds[0] = plane.D; + + // Far + plane = view.Frustum.GetFar(); + cullData.xs[1] = plane.Normal.X; + cullData.ys[1] = plane.Normal.Y; + cullData.zs[1] = plane.Normal.Z; + cullData.ds[1] = plane.D; + + // Left + plane = view.Frustum.GetLeft(); + cullData.xs[2] = plane.Normal.X; + cullData.ys[2] = plane.Normal.Y; + cullData.zs[2] = plane.Normal.Z; + cullData.ds[2] = plane.D; + + // Right + plane = view.Frustum.GetRight(); + cullData.xs[3] = plane.Normal.X; + cullData.ys[3] = plane.Normal.Y; + cullData.zs[3] = plane.Normal.Z; + cullData.ds[3] = plane.D; + + // Top + plane = view.Frustum.GetTop(); + cullData.xs[4] = plane.Normal.X; + cullData.ys[4] = plane.Normal.Y; + cullData.zs[4] = plane.Normal.Z; + cullData.ds[4] = plane.D; + + // Bottom + plane = view.Frustum.GetBottom(); + cullData.xs[5] = plane.Normal.X; + cullData.ys[5] = plane.Normal.Y; + cullData.zs[5] = plane.Normal.Z; + cullData.ds[5] = plane.D; + + // Extra 0 + cullData.xs[6] = 0; + cullData.ys[6] = 0; + cullData.zs[6] = 0; + cullData.ds[6] = 0; + + // Extra 1 + cullData.xs[7] = 0; + cullData.ys[7] = 0; + cullData.zs[7] = 0; + cullData.ds[7] = 0; + } + + SimdVector4 px = SIMD::Load(cullData.xs); + SimdVector4 py = SIMD::Load(cullData.ys); + SimdVector4 pz = SIMD::Load(cullData.zs); + SimdVector4 pd = SIMD::Load(cullData.ds); + SimdVector4 px2 = SIMD::Load(&cullData.xs[4]); + SimdVector4 py2 = SIMD::Load(&cullData.ys[4]); + SimdVector4 pz2 = SIMD::Load(&cullData.zs[4]); + SimdVector4 pd2 = SIMD::Load(&cullData.ds[4]); + + for (int32 i = 0; i < List.Count(); i++) + { + auto e = List[i]; + + SimdVector4 cx = SIMD::Splat(e.Bounds.Center.X); + SimdVector4 cy = SIMD::Splat(e.Bounds.Center.Y); + SimdVector4 cz = SIMD::Splat(e.Bounds.Center.Z); + SimdVector4 r = SIMD::Splat(-e.Bounds.Radius); + + SimdVector4 t = SIMD::Mul(cx, px); + t = SIMD::Add(t, SIMD::Mul(cy, py)); + t = SIMD::Add(t, SIMD::Mul(cz, pz)); + t = SIMD::Add(t, pd); + t = SIMD::Sub(t, r); + if (SIMD::MoveMask(t)) + continue; + + t = SIMD::Mul(cx, px2); + t = SIMD::Add(t, SIMD::Mul(cy, py2)); + t = SIMD::Add(t, SIMD::Mul(cz, pz2)); + t = SIMD::Add(t, pd2); + t = SIMD::Sub(t, r); + if (SIMD::MoveMask(t)) + continue; + + if (view.RenderLayersMask.Mask & e.LayerMask && e.Actor->GetStaticFlags() & renderContext.View.StaticFlagsMask) + { +#if SCENE_RENDERING_USE_PROFILER + PROFILE_CPU(); + ___tracy_scoped_zone.Name(*e.Actor->GetName(), e.Actor->GetName().Length()); +#endif + e.Actor->Draw(renderContext); + } + } +#else + for (int32 i = 0; i < List.Count(); i++) + { + auto e = List[i]; + if (view.RenderLayersMask.Mask & e.LayerMask && frustum.Intersects(e.Bounds) && e.Actor->GetStaticFlags() & view.StaticFlagsMask) + { +#if SCENE_RENDERING_USE_PROFILER + PROFILE_CPU(); + ___tracy_scoped_zone.Name(*e.Actor->GetName(), e.Actor->GetName().Length()); +#endif + e.Actor->Draw(renderContext); + } + } +#endif +} + SceneRendering::SceneRendering(::Scene* scene) : Scene(scene) { } -void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, const Array& actors) -{ - auto& view = renderContext.View; -#if SCENE_RENDERING_USE_SIMD - CullDataSIMD cullData; - { - // Near - auto plane = view.Frustum.GetNear(); - cullData.xs[0] = plane.Normal.X; - cullData.ys[0] = plane.Normal.Y; - cullData.zs[0] = plane.Normal.Z; - cullData.ds[0] = plane.D; - - // Far - plane = view.Frustum.GetFar(); - cullData.xs[1] = plane.Normal.X; - cullData.ys[1] = plane.Normal.Y; - cullData.zs[1] = plane.Normal.Z; - cullData.ds[1] = plane.D; - - // Left - plane = view.Frustum.GetLeft(); - cullData.xs[2] = plane.Normal.X; - cullData.ys[2] = plane.Normal.Y; - cullData.zs[2] = plane.Normal.Z; - cullData.ds[2] = plane.D; - - // Right - plane = view.Frustum.GetRight(); - cullData.xs[3] = plane.Normal.X; - cullData.ys[3] = plane.Normal.Y; - cullData.zs[3] = plane.Normal.Z; - cullData.ds[3] = plane.D; - - // Top - plane = view.Frustum.GetTop(); - cullData.xs[4] = plane.Normal.X; - cullData.ys[4] = plane.Normal.Y; - cullData.zs[4] = plane.Normal.Z; - cullData.ds[4] = plane.D; - - // Bottom - plane = view.Frustum.GetBottom(); - cullData.xs[5] = plane.Normal.X; - cullData.ys[5] = plane.Normal.Y; - cullData.zs[5] = plane.Normal.Z; - cullData.ds[5] = plane.D; - - // Extra 0 - cullData.xs[6] = 0; - cullData.ys[6] = 0; - cullData.zs[6] = 0; - cullData.ds[6] = 0; - - // Extra 1 - cullData.xs[7] = 0; - cullData.ys[7] = 0; - cullData.zs[7] = 0; - cullData.ds[7] = 0; - } - - float4 px = SIMD::Load(cullData.xs); - float4 py = SIMD::Load(cullData.ys); - float4 pz = SIMD::Load(cullData.zs); - float4 pd = SIMD::Load(cullData.ds); - float4 px2 = SIMD::Load(&cullData.xs[4]); - float4 py2 = SIMD::Load(&cullData.ys[4]); - float4 pz2 = SIMD::Load(&cullData.zs[4]); - float4 pd2 = SIMD::Load(&cullData.ds[4]); - - for (int32 i = 0; i < actors.Count(); i++) - { - const auto& sphere = actors[i]->GetSphere(); - float4 cx = SIMD::Splat(sphere.Center.X); - float4 cy = SIMD::Splat(sphere.Center.Y); - float4 cz = SIMD::Splat(sphere.Center.Z); - float4 r = SIMD::Splat(-sphere.Radius); - - float4 t = SIMD::Mul(cx, px); - t = SIMD::Add(t, SIMD::Mul(cy, py)); - t = SIMD::Add(t, SIMD::Mul(cz, pz)); - t = SIMD::Add(t, pd); - t = SIMD::Sub(t, r); - if (SIMD::MoveMask(t)) - continue; - - t = SIMD::Mul(cx, px2); - t = SIMD::Add(t, SIMD::Mul(cy, py2)); - t = SIMD::Add(t, SIMD::Mul(cz, pz2)); - t = SIMD::Add(t, pd2); - t = SIMD::Sub(t, r); - if (SIMD::MoveMask(t)) - continue; - - actors[i]->Draw(renderContext); - } -#else - for (int32 i = 0; i < actors.Count(); i++) - { - auto actor = actors[i]; - if (view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere())) - actor->Draw(renderContext); - } -#endif -} - -void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderContext, const Array& actors) -{ - auto& view = renderContext.View; -#if SCENE_RENDERING_USE_SIMD - CullDataSIMD cullData; - { - // Near - auto plane = view.Frustum.GetNear(); - cullData.xs[0] = plane.Normal.X; - cullData.ys[0] = plane.Normal.Y; - cullData.zs[0] = plane.Normal.Z; - cullData.ds[0] = plane.D; - - // Far - plane = view.Frustum.GetFar(); - cullData.xs[1] = plane.Normal.X; - cullData.ys[1] = plane.Normal.Y; - cullData.zs[1] = plane.Normal.Z; - cullData.ds[1] = plane.D; - - // Left - plane = view.Frustum.GetLeft(); - cullData.xs[2] = plane.Normal.X; - cullData.ys[2] = plane.Normal.Y; - cullData.zs[2] = plane.Normal.Z; - cullData.ds[2] = plane.D; - - // Right - plane = view.Frustum.GetRight(); - cullData.xs[3] = plane.Normal.X; - cullData.ys[3] = plane.Normal.Y; - cullData.zs[3] = plane.Normal.Z; - cullData.ds[3] = plane.D; - - // Top - plane = view.Frustum.GetTop(); - cullData.xs[4] = plane.Normal.X; - cullData.ys[4] = plane.Normal.Y; - cullData.zs[4] = plane.Normal.Z; - cullData.ds[4] = plane.D; - - // Bottom - plane = view.Frustum.GetBottom(); - cullData.xs[5] = plane.Normal.X; - cullData.ys[5] = plane.Normal.Y; - cullData.zs[5] = plane.Normal.Z; - cullData.ds[5] = plane.D; - - // Extra 0 - cullData.xs[6] = 0; - cullData.ys[6] = 0; - cullData.zs[6] = 0; - cullData.ds[6] = 0; - - // Extra 1 - cullData.xs[7] = 0; - cullData.ys[7] = 0; - cullData.zs[7] = 0; - cullData.ds[7] = 0; - } - - float4 px = SIMD::Load(cullData.xs); - float4 py = SIMD::Load(cullData.ys); - float4 pz = SIMD::Load(cullData.zs); - float4 pd = SIMD::Load(cullData.ds); - float4 px2 = SIMD::Load(&cullData.xs[4]); - float4 py2 = SIMD::Load(&cullData.ys[4]); - float4 pz2 = SIMD::Load(&cullData.zs[4]); - float4 pd2 = SIMD::Load(&cullData.ds[4]); - - for (int32 i = 0; i < actors.Count(); i++) - { - const auto& sphere = actors[i]->GetSphere(); - float4 cx = SIMD::Splat(sphere.Center.X); - float4 cy = SIMD::Splat(sphere.Center.Y); - float4 cz = SIMD::Splat(sphere.Center.Z); - float4 r = SIMD::Splat(-sphere.Radius); - - float4 t = SIMD::Mul(cx, px); - t = SIMD::Add(t, SIMD::Mul(cy, py)); - t = SIMD::Add(t, SIMD::Mul(cz, pz)); - t = SIMD::Add(t, pd); - t = SIMD::Sub(t, r); - if (SIMD::MoveMask(t)) - continue; - - t = SIMD::Mul(cx, px2); - t = SIMD::Add(t, SIMD::Mul(cy, py2)); - t = SIMD::Add(t, SIMD::Mul(cz, pz2)); - t = SIMD::Add(t, pd2); - t = SIMD::Sub(t, r); - if (SIMD::MoveMask(t)) - continue; - - if (actors[i]->GetStaticFlags() & renderContext.View.StaticFlagsMask) - actors[i]->Draw(renderContext); - } -#else - for (int32 i = 0; i < actors.Count(); i++) - { - auto actor = actors[i]; - if (actor->GetStaticFlags() & view.StaticFlagsMask && view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere())) - actor->Draw(renderContext); - } -#endif -} - void SceneRendering::Draw(RenderContext& renderContext) { // Skip if disabled @@ -248,13 +321,12 @@ void SceneRendering::Draw(RenderContext& renderContext) auto& view = renderContext.View; // Draw all visual components - const BoundingFrustum frustum = view.CullingFrustum; if (view.IsOfflinePass) { - CullAndDrawOffline(frustum, renderContext, Geometry); + Geometry.CullAndDrawOffline(renderContext); if (view.Pass & DrawPass::GBuffer) { - CullAndDrawOffline(frustum, renderContext, Common); + Common.CullAndDrawOffline(renderContext); for (int32 i = 0; i < CommonNoCulling.Count(); i++) { auto actor = CommonNoCulling[i]; @@ -265,15 +337,21 @@ void SceneRendering::Draw(RenderContext& renderContext) } else { - CullAndDraw(frustum, renderContext, Geometry); + Geometry.CullAndDraw(renderContext); if (view.Pass & DrawPass::GBuffer) { - CullAndDraw(frustum, renderContext, Common); + Common.CullAndDraw(renderContext); for (int32 i = 0; i < CommonNoCulling.Count(); i++) { auto actor = CommonNoCulling[i]; if (view.RenderLayersMask.HasLayer(actor->GetLayer())) + { +#if SCENE_RENDERING_USE_PROFILER + PROFILE_CPU(); + ___tracy_scoped_zone.Name(*actor->GetName(), actor->GetName().Length()); +#endif actor->Draw(renderContext); + } } } } @@ -294,6 +372,9 @@ void SceneRendering::Draw(RenderContext& renderContext) void SceneRendering::CollectPostFxVolumes(RenderContext& renderContext) { +#if SCENE_RENDERING_USE_PROFILER + PROFILE_CPU(); +#endif for (int32 i = 0; i < PostFxProviders.Count(); i++) { PostFxProviders[i]->Collect(renderContext); diff --git a/Source/Engine/Level/Scene/SceneRendering.h b/Source/Engine/Level/Scene/SceneRendering.h index ca76b88d8..7a4bc68d5 100644 --- a/Source/Engine/Level/Scene/SceneRendering.h +++ b/Source/Engine/Level/Scene/SceneRendering.h @@ -4,6 +4,8 @@ #include "Engine/Core/Delegate.h" #include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Math/BoundingSphere.h" +#include "Engine/Level/Actor.h" #include "Engine/Level/Types.h" class SceneRenderTask; @@ -14,7 +16,7 @@ struct RenderView; /// /// Interface for actors that can override the default rendering settings (eg. PostFxVolume actor). /// -class IPostFxSettingsProvider +class FLAXENGINE_API IPostFxSettingsProvider { public: @@ -35,24 +37,37 @@ public: /// /// Scene rendering helper subsystem that boosts the level rendering by providing efficient objects cache and culling implementation. /// -class SceneRendering +class FLAXENGINE_API SceneRendering { friend Scene; - #if USE_EDITOR typedef Function PhysicsDebugCallback; + friend class ViewportIconsRendererService; #endif + struct DrawEntry + { + Actor* Actor; + uint32 LayerMask; + BoundingSphere Bounds; + }; + + struct DrawEntries + { + Array List; + + int32 Add(Actor* obj); + void Update(Actor* obj, int32 key); + void Remove(Actor* obj, int32 key); + void Clear(); + void CullAndDraw(RenderContext& renderContext); + void CullAndDrawOffline(RenderContext& renderContext); + }; private: - // Private data Scene* Scene; - -public: - - // Things to draw - Array Geometry; - Array Common; + DrawEntries Geometry; + DrawEntries Common; Array CommonNoCulling; Array PostFxProviders; #if USE_EDITOR @@ -83,24 +98,36 @@ public: public: - FORCE_INLINE void AddGeometry(Actor* obj) + FORCE_INLINE int32 AddGeometry(Actor* obj) { - Geometry.Add(obj); + return Geometry.Add(obj); } - FORCE_INLINE void RemoveGeometry(Actor* obj) + FORCE_INLINE void UpdateGeometry(Actor* obj, int32 key) { - Geometry.Remove(obj); + Geometry.Update(obj, key); } - FORCE_INLINE void AddCommon(Actor* obj) + FORCE_INLINE void RemoveGeometry(Actor* obj, int32& key) { - Common.Add(obj); + Geometry.Remove(obj, key); + key = -1; } - FORCE_INLINE void RemoveCommon(Actor* obj) + FORCE_INLINE int32 AddCommon(Actor* obj) { - Common.Remove(obj); + return Common.Add(obj); + } + + FORCE_INLINE void UpdateCommon(Actor* obj, int32 key) + { + Common.Update(obj, key); + } + + FORCE_INLINE void RemoveCommon(Actor* obj, int32& key) + { + Common.Remove(obj, key); + key = -1; } FORCE_INLINE void AddCommonNoCulling(Actor* obj) diff --git a/Source/Engine/Level/Scene/SceneTicking.cpp b/Source/Engine/Level/Scene/SceneTicking.cpp index 862e3a352..b3a6c1cdc 100644 --- a/Source/Engine/Level/Scene/SceneTicking.cpp +++ b/Source/Engine/Level/Scene/SceneTicking.cpp @@ -4,6 +4,12 @@ #include "Scene.h" #include "Engine/Scripting/Script.h" +SceneTicking::TickData::TickData(int32 capacity) + : Scripts(capacity) + , Ticks(capacity) +{ +} + void SceneTicking::TickData::AddScript(Script* script) { Scripts.Add(script); @@ -22,6 +28,65 @@ void SceneTicking::TickData::RemoveScript(Script* script) #endif } +void SceneTicking::TickData::RemoveTick(void* callee) +{ + for (int32 i = 0; i < Ticks.Count(); i++) + { + if (Ticks[i].Callee == callee) + { + Ticks.RemoveAt(i); + break; + } + } +} + +void SceneTicking::TickData::Tick() +{ + TickScripts(Scripts); + + for (int32 i = 0; i < Ticks.Count(); i++) + Ticks[i].Call(); +} + +#if USE_EDITOR + +void SceneTicking::TickData::RemoveTickExecuteInEditor(void* callee) +{ + for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++) + { + if (TicksExecuteInEditor[i].Callee == callee) + { + TicksExecuteInEditor.RemoveAt(i); + break; + } + } +} + +void SceneTicking::TickData::TickExecuteInEditor() +{ + TickScripts(ScriptsExecuteInEditor); + + for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++) + TicksExecuteInEditor[i].Call(); +} + +#endif + +void SceneTicking::TickData::Clear() +{ + Scripts.Clear(); + Ticks.Clear(); +#if USE_EDITOR + ScriptsExecuteInEditor.Clear(); + TicksExecuteInEditor.Clear(); +#endif +} + +SceneTicking::FixedUpdateTickData::FixedUpdateTickData() + : TickData(512) +{ +} + void SceneTicking::FixedUpdateTickData::TickScripts(const Array& scripts) { for (auto* script : scripts) @@ -30,6 +95,11 @@ void SceneTicking::FixedUpdateTickData::TickScripts(const Array& script } } +SceneTicking::UpdateTickData::UpdateTickData() + : TickData(1024) +{ +} + void SceneTicking::UpdateTickData::TickScripts(const Array& scripts) { for (auto* script : scripts) @@ -38,6 +108,11 @@ void SceneTicking::UpdateTickData::TickScripts(const Array& scripts) } } +SceneTicking::LateUpdateTickData::LateUpdateTickData() + : TickData(64) +{ +} + void SceneTicking::LateUpdateTickData::TickScripts(const Array& scripts) { for (auto* script : scripts) @@ -46,15 +121,9 @@ void SceneTicking::LateUpdateTickData::TickScripts(const Array& scripts } } -SceneTicking::SceneTicking(::Scene* scene) - : Scene(scene) -{ -} - void SceneTicking::AddScript(Script* obj) { - ASSERT(obj && obj->GetParent() && obj->GetParent()->GetScene() == Scene); - + ASSERT_LOW_LAYER(obj && obj->GetParent() && obj->GetParent()->GetScene()); if (obj->_tickFixedUpdate) FixedUpdate.AddScript(obj); if (obj->_tickUpdate) @@ -65,8 +134,7 @@ void SceneTicking::AddScript(Script* obj) void SceneTicking::RemoveScript(Script* obj) { - ASSERT(obj && obj->GetParent() && obj->GetParent()->GetScene() == Scene); - + ASSERT_LOW_LAYER(obj && obj->GetParent() && obj->GetParent()->GetScene()); if (obj->_tickFixedUpdate) FixedUpdate.RemoveScript(obj); if (obj->_tickUpdate) diff --git a/Source/Engine/Level/Scene/SceneTicking.h b/Source/Engine/Level/Scene/SceneTicking.h index e8fbe8236..b90897792 100644 --- a/Source/Engine/Level/Scene/SceneTicking.h +++ b/Source/Engine/Level/Scene/SceneTicking.h @@ -10,8 +10,6 @@ /// class FLAXENGINE_API SceneTicking { - friend Scene; - public: /// @@ -28,13 +26,9 @@ public: (static_cast(callee)->*Method)(); } - public: - void* Callee; SignatureObj FunctionObj; - public: - template void Bind(T* callee) { @@ -42,15 +36,15 @@ public: FunctionObj = &MethodCaller; } - /// - /// Calls the binded function. - /// FORCE_INLINE void Call() const { (*FunctionObj)(Callee); } }; + /// + /// Ticking data container. + /// class FLAXENGINE_API TickData { public: @@ -62,32 +56,10 @@ public: Array TicksExecuteInEditor; #endif - TickData(int32 capacity) - : Scripts(capacity) - , Ticks(capacity) - { - } + TickData(int32 capacity); virtual void TickScripts(const Array& scripts) = 0; - void Tick() - { - TickScripts(Scripts); - - for (int32 i = 0; i < Ticks.Count(); i++) - Ticks[i].Call(); - } - -#if USE_EDITOR - void TickExecuteInEditor() - { - TickScripts(ScriptsExecuteInEditor); - - for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++) - TicksExecuteInEditor[i].Call(); - } -#endif - void AddScript(Script* script); void RemoveScript(Script* script); @@ -99,17 +71,8 @@ public: Ticks.Add(tick); } - void RemoveTick(void* callee) - { - for (int32 i = 0; i < Ticks.Count(); i++) - { - if (Ticks[i].Callee == callee) - { - Ticks.RemoveAt(i); - break; - } - } - } + void RemoveTick(void* callee); + void Tick(); #if USE_EDITOR template @@ -120,39 +83,18 @@ public: TicksExecuteInEditor.Add(tick); } - void RemoveTickExecuteInEditor(void* callee) - { - for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++) - { - if (TicksExecuteInEditor[i].Callee == callee) - { - TicksExecuteInEditor.RemoveAt(i); - break; - } - } - } + void RemoveTickExecuteInEditor(void* callee); + void TickExecuteInEditor(); #endif - void Clear() - { - Scripts.Clear(); - Ticks.Clear(); -#if USE_EDITOR - ScriptsExecuteInEditor.Clear(); - TicksExecuteInEditor.Clear(); -#endif - } + void Clear(); }; class FLAXENGINE_API FixedUpdateTickData : public TickData { public: - FixedUpdateTickData() - : TickData(512) - { - } - + FixedUpdateTickData(); void TickScripts(const Array& scripts) override; }; @@ -160,11 +102,7 @@ public: { public: - UpdateTickData() - : TickData(1024) - { - } - + UpdateTickData(); void TickScripts(const Array& scripts) override; }; @@ -172,20 +110,10 @@ public: { public: - LateUpdateTickData() - : TickData(64) - { - } - + LateUpdateTickData(); void TickScripts(const Array& scripts) override; }; -private: - - Scene* Scene; - - explicit SceneTicking(::Scene* scene); - public: /// diff --git a/Source/Engine/Navigation/NavMesh.cpp b/Source/Engine/Navigation/NavMesh.cpp index 32ba8c61f..1f78287ae 100644 --- a/Source/Engine/Navigation/NavMesh.cpp +++ b/Source/Engine/Navigation/NavMesh.cpp @@ -156,14 +156,14 @@ void NavMesh::OnEnable() // Base Actor::OnEnable(); - GetScene()->NavigationMeshes.Add(this); + GetScene()->Navigation.Meshes.Add(this); AddTiles(); } void NavMesh::OnDisable() { RemoveTiles(); - GetScene()->NavigationMeshes.Remove(this); + GetScene()->Navigation.Meshes.Remove(this); // Base Actor::OnDisable(); diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.cpp b/Source/Engine/Navigation/NavMeshBoundsVolume.cpp index 700442b2f..1e11ffe84 100644 --- a/Source/Engine/Navigation/NavMeshBoundsVolume.cpp +++ b/Source/Engine/Navigation/NavMeshBoundsVolume.cpp @@ -37,12 +37,12 @@ void NavMeshBoundsVolume::OnEnable() // Base Actor::OnEnable(); - GetScene()->NavigationVolumes.Add(this); + GetScene()->Navigation.Volumes.Add(this); } void NavMeshBoundsVolume::OnDisable() { - GetScene()->NavigationVolumes.Remove(this); + GetScene()->Navigation.Volumes.Remove(this); // Base Actor::OnDisable(); diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 755f4179b..8187de90a 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -348,9 +348,9 @@ bool GetNavMeshTileBounds(Scene* scene, NavMesh* navMesh, int32 x, int32 y, floa // Check if any navmesh volume intersects with the tile bool foundAnyVolume = false; Vector2 rangeY; - for (int32 i = 0; i < scene->NavigationVolumes.Count(); i++) + for (int32 i = 0; i < scene->Navigation.Volumes.Count(); i++) { - const auto volume = scene->NavigationVolumes[i]; + const auto volume = scene->Navigation.Volumes[i]; if (!volume->AgentsMask.IsNavMeshSupported(navMesh->Properties)) continue; const auto& volumeBounds = volume->GetBox(); @@ -925,7 +925,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild for (auto& navMeshProperties : settings->NavMeshes) { NavMesh* navMesh = nullptr; - for (auto e : scene->NavigationMeshes) + for (auto e : scene->Navigation.Meshes) { if (e->Properties.Name == navMeshProperties.Name) { @@ -953,7 +953,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild } // Build all navmeshes on the scene - for (NavMesh* navMesh : scene->NavigationMeshes) + for (NavMesh* navMesh : scene->Navigation.Meshes) { BuildDirtyBounds(scene, navMesh, dirtyBounds, rebuild); } @@ -961,7 +961,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild // Remove unused navmeshes if (settings->AutoRemoveMissingNavMeshes) { - for (NavMesh* navMesh : scene->NavigationMeshes) + for (NavMesh* navMesh : scene->Navigation.Meshes) { // Skip used navmeshes if (navMesh->Data.Tiles.HasItems()) @@ -987,7 +987,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild void BuildWholeScene(Scene* scene) { // Compute total navigation area bounds - const BoundingBox worldBounds = scene->GetNavigationBounds(); + const BoundingBox worldBounds = scene->Navigation.GetNavigationBounds(); BuildDirtyBounds(scene, worldBounds, true); } @@ -995,7 +995,7 @@ void BuildWholeScene(Scene* scene) void ClearNavigation(Scene* scene) { const bool autoRemoveMissingNavMeshes = NavigationSettings::Get()->AutoRemoveMissingNavMeshes; - for (NavMesh* navMesh : scene->NavigationMeshes) + for (NavMesh* navMesh : scene->Navigation.Meshes) { navMesh->ClearData(); if (autoRemoveMissingNavMeshes) @@ -1020,7 +1020,7 @@ void NavMeshBuilder::Update() continue; // Early out if scene has no bounds volumes to define nav mesh area - if (scene->NavigationVolumes.IsEmpty()) + if (scene->Navigation.Volumes.IsEmpty()) { ClearNavigation(scene); continue; @@ -1042,7 +1042,7 @@ void NavMeshBuilder::Update() void NavMeshBuilder::Build(Scene* scene, float timeoutMs) { // Early out if scene is not using navigation - if (scene->NavigationVolumes.IsEmpty()) + if (scene->Navigation.Volumes.IsEmpty()) { ClearNavigation(scene); return; @@ -1073,7 +1073,7 @@ void NavMeshBuilder::Build(Scene* scene, float timeoutMs) void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs) { // Early out if scene is not using navigation - if (scene->NavigationVolumes.IsEmpty()) + if (scene->Navigation.Volumes.IsEmpty()) { ClearNavigation(scene); return; diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index d7fdcfc33..2561a2176 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -371,7 +371,7 @@ void Navigation::DrawNavMesh() bool skip = false; for (auto scene : Level::Scenes) { - for (auto e : scene->NavigationMeshes) + for (auto e : scene->Navigation.Meshes) { if (e->Properties == navMesh->Properties) { diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 309b760fe..7b8f599fe 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -311,6 +311,8 @@ void ParticleEffect::UpdateBounds() _box = bounds; BoundingSphere::FromBox(bounds, _sphere); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } void ParticleEffect::Sync() @@ -515,6 +517,12 @@ void ParticleEffect::OnDebugDrawSelected() #endif +void ParticleEffect::OnLayerChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); +} + void ParticleEffect::Serialize(SerializeStream& stream, const void* otherObj) { // Base @@ -691,7 +699,7 @@ void ParticleEffect::EndPlay() void ParticleEffect::OnEnable() { GetScene()->Ticking.Update.AddTick(this); - GetSceneRendering()->AddGeometry(this); + _sceneRenderingKey = GetSceneRendering()->AddGeometry(this); #if USE_EDITOR GetSceneRendering()->AddViewportIcon(this); GetScene()->Ticking.Update.AddTickExecuteInEditor(this); @@ -707,7 +715,7 @@ void ParticleEffect::OnDisable() GetScene()->Ticking.Update.RemoveTickExecuteInEditor(this); GetSceneRendering()->RemoveViewportIcon(this); #endif - GetSceneRendering()->RemoveGeometry(this); + GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey); GetScene()->Ticking.Update.RemoveTick(this); // Base diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h index f6dc3788a..ac9a43a91 100644 --- a/Source/Engine/Particles/ParticleEffect.h +++ b/Source/Engine/Particles/ParticleEffect.h @@ -184,6 +184,7 @@ private: uint64 _lastUpdateFrame; float _lastMinDstSqr; Matrix _world; + int32 _sceneRenderingKey = -1; uint32 _parametersVersion = 0; // Version number for _parameters to be in sync with Instance.ParametersVersion Array _parameters; // Cached for scripting API Array _parametersOverrides; // Cached parameter modifications to be applied to the parameters @@ -388,6 +389,7 @@ public: #if USE_EDITOR void OnDebugDrawSelected() override; #endif + void OnLayerChanged() override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; #if USE_EDITOR diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index b92985a8f..f387f5085 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -361,8 +361,6 @@ void RenderList::Clear() void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals) { - ASSERT_LOW_LAYER(drawCall.Geometry.IndexBuffer); - // Mix object mask with material mask const auto mask = (DrawPass)(drawModes & drawCall.Material->GetDrawModes()); if (mask == DrawPass::None) diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index 8aff910f3..58be6f73c 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -47,6 +47,8 @@ void Terrain::UpdateBounds() BoundingBox::Merge(_box, patch->_bounds, _box); } BoundingSphere::FromBox(_box, _sphere); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } void Terrain::CacheNeighbors() @@ -752,7 +754,7 @@ RigidBody* Terrain::GetAttachedRigidBody() const void Terrain::OnEnable() { - GetSceneRendering()->AddGeometry(this); + _sceneRenderingKey = GetSceneRendering()->AddGeometry(this); #if TERRAIN_USE_PHYSICS_DEBUG GetSceneRendering()->AddPhysicsDebug(this); #endif @@ -763,7 +765,7 @@ void Terrain::OnEnable() void Terrain::OnDisable() { - GetSceneRendering()->RemoveGeometry(this); + GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey); #if TERRAIN_USE_PHYSICS_DEBUG GetSceneRendering()->RemovePhysicsDebug(this); #endif @@ -803,6 +805,8 @@ void Terrain::OnLayerChanged() Actor::OnLayerChanged(); UpdateLayerBits(); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } void Terrain::OnActiveInTreeChanged() diff --git a/Source/Engine/Terrain/Terrain.h b/Source/Engine/Terrain/Terrain.h index 5ad8378e9..2800b58b1 100644 --- a/Source/Engine/Terrain/Terrain.h +++ b/Source/Engine/Terrain/Terrain.h @@ -53,6 +53,7 @@ private: char _collisionLod; byte _lodCount; uint16 _chunkSize; + int32 _sceneRenderingKey = -1; float _scaleInLightmap; float _lodDistribution; Vector3 _boundsExtent; @@ -444,6 +445,7 @@ public: #if USE_EDITOR void OnDebugDrawSelected() override; #endif + void OnLayerChanged() override; bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; @@ -455,7 +457,6 @@ protected: void OnEnable() override; void OnDisable() override; void OnTransformChanged() override; - void OnLayerChanged() override; void OnActiveInTreeChanged() override; void BeginPlay(SceneBeginData* data) override; void EndPlay() override; diff --git a/Source/Engine/UI/SpriteRender.cpp b/Source/Engine/UI/SpriteRender.cpp index e82c523c3..985b8b2da 100644 --- a/Source/Engine/UI/SpriteRender.cpp +++ b/Source/Engine/UI/SpriteRender.cpp @@ -170,6 +170,12 @@ void SpriteRender::Deserialize(DeserializeStream& stream, ISerializeModifier* mo _paramColor->SetValue(_color); } +void SpriteRender::OnLayerChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); +} + void SpriteRender::OnEndPlay() { // Base @@ -187,7 +193,7 @@ void SpriteRender::OnEndPlay() void SpriteRender::OnEnable() { - GetSceneRendering()->AddGeometry(this); + _sceneRenderingKey = GetSceneRendering()->AddGeometry(this); // Base Actor::OnEnable(); @@ -195,7 +201,7 @@ void SpriteRender::OnEnable() void SpriteRender::OnDisable() { - GetSceneRendering()->RemoveGeometry(this); + GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey); // Base Actor::OnDisable(); @@ -211,4 +217,6 @@ void SpriteRender::OnTransformChanged() _transform.GetWorld(world); BoundingSphere::Transform(localSphere, world, _sphere); BoundingBox::FromSphere(_sphere, _box); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } diff --git a/Source/Engine/UI/SpriteRender.h b/Source/Engine/UI/SpriteRender.h index 9bd9eeaf0..6965b1317 100644 --- a/Source/Engine/UI/SpriteRender.h +++ b/Source/Engine/UI/SpriteRender.h @@ -23,6 +23,7 @@ private: MaterialParameter* _paramImageMAD = nullptr; MaterialParameter* _paramColor = nullptr; AssetReference _quadModel; + int32 _sceneRenderingKey = -1; public: @@ -97,6 +98,7 @@ public: void DrawGeneric(RenderContext& renderContext) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void OnLayerChanged() override; void OnEndPlay() override; protected: diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index 30335aee3..512f91d93 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -329,6 +329,8 @@ void TextRender::UpdateLayout() _localBox = box; BoundingBox::Transform(_localBox, _world, _box); BoundingSphere::FromBox(_box, _sphere); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } bool TextRender::HasContentLoaded() const @@ -421,6 +423,12 @@ void TextRender::OnDebugDrawSelected() #endif +void TextRender::OnLayerChanged() +{ + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); +} + bool TextRender::IntersectsItself(const Ray& ray, float& distance, Vector3& normal) { #if USE_PRECISE_MESH_INTERSECTS @@ -480,8 +488,6 @@ void TextRender::Deserialize(DeserializeStream& stream, ISerializeModifier* modi void TextRender::OnEnable() { - GetSceneRendering()->AddGeometry(this); - // Base Actor::OnEnable(); @@ -489,6 +495,7 @@ void TextRender::OnEnable() { UpdateLayout(); } + _sceneRenderingKey = GetSceneRendering()->AddGeometry(this); } void TextRender::OnDisable() @@ -498,7 +505,7 @@ void TextRender::OnDisable() _isLocalized = false; Localization::LocalizationChanged.Unbind(this); } - GetSceneRendering()->RemoveGeometry(this); + GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey); // Base Actor::OnDisable(); @@ -512,4 +519,6 @@ void TextRender::OnTransformChanged() _transform.GetWorld(_world); BoundingBox::Transform(_localBox, _world, _box); BoundingSphere::FromBox(_box, _sphere); + if (_sceneRenderingKey != -1) + GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey); } diff --git a/Source/Engine/UI/TextRender.h b/Source/Engine/UI/TextRender.h index fff3915a5..8d01fec49 100644 --- a/Source/Engine/UI/TextRender.h +++ b/Source/Engine/UI/TextRender.h @@ -40,6 +40,7 @@ private: Color _color; TextLayoutOptions _layoutOptions; int32 _size; + int32 _sceneRenderingKey = -1; BoundingBox _localBox; Matrix _world; @@ -169,6 +170,7 @@ public: #if USE_EDITOR void OnDebugDrawSelected() override; #endif + void OnLayerChanged() override; bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;