diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 116866848..efe89bf5c 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -6,14 +6,13 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Random.h" #include "Engine/Engine/Engine.h" +#include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/RenderTask.h" +#include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Content/Deprecated.h" #if !FOLIAGE_USE_SINGLE_QUAD_TREE #include "Engine/Threading/JobSystem.h" -#if FOLIAGE_USE_DRAW_CALLS_BATCHING -#include "Engine/Graphics/RenderTools.h" -#endif #endif #include "Engine/Level/SceneQuery.h" #include "Engine/Profiler/ProfilerCPU.h" @@ -125,7 +124,7 @@ void Foliage::AddToCluster(ChunkedArrayLODs.Get()[lod].Meshes; for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++) @@ -141,7 +140,7 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan auto* e = result.TryGet(key); if (!e) { - e = &result.Add(key, BatchedDrawCall(renderContext.List))->Value; + e = &result.Add(key, BatchedDrawCall(context.RenderContext.List))->Value; ASSERT_LOW_LAYER(key.Mat); e->DrawCall.Material = key.Mat; e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr; @@ -152,21 +151,18 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan auto& instanceData = e->Instances.AddOne(); Matrix world; const Transform transform = _transform.LocalToWorld(instance.Transform); - const Float3 translation = transform.Translation - renderContext.View.Origin; + const Float3 translation = transform.Translation - context.ViewOrigin; Matrix::Transformation(transform.Scale, transform.Orientation, translation, world); constexpr float worldDeterminantSign = 1.0f; instanceData.Store(world, world, instance.Lightmap.UVsArea, drawCall.Surface.GeometrySize, instance.Random, worldDeterminantSign, lodDitherFactor); } } -void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const +void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const { // Skip clusters that around too far from view - const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); - if (Float3::Distance(lodView->Position, cluster->TotalBoundsSphere.Center - lodView->Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) + if (Float3::Distance(context.LodView.Position, cluster->TotalBoundsSphere.Center - context.LodView.Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) return; - const Vector3 viewOrigin = renderContext.View.Origin; - //DebugDraw::DrawBox(cluster->Bounds, Color::Red); // Draw visible children @@ -178,10 +174,10 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, BoundingBox box; #define DRAW_CLUSTER(idx) \ box = cluster->Children[idx]->TotalBounds; \ - box.Minimum -= viewOrigin; \ - box.Maximum -= viewOrigin; \ - if (renderContext.View.CullingFrustum.Intersects(box)) \ - DrawCluster(renderContext, cluster->Children[idx], type, drawCallsLists, result) + box.Minimum -= context.ViewOrigin; \ + box.Maximum -= context.ViewOrigin; \ + if (context.RenderContext.View.CullingFrustum.Intersects(box)) \ + DrawCluster(context, cluster->Children[idx], drawCallsLists, result) DRAW_CLUSTER(0); DRAW_CLUSTER(1); DRAW_CLUSTER(2); @@ -192,21 +188,22 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, { // Draw visible instances const auto frame = Engine::FrameCount; - const auto model = type.Model.Get(); - const auto transitionLOD = renderContext.View.Pass != DrawPass::Depth; // Let the main view pass update LOD transitions + const auto model = context.FoliageType.Model.Get(); + const auto transitionLOD = context.RenderContext.View.Pass != DrawPass::Depth; // Let the main view pass update LOD transitions // TODO: move DrawState to be stored per-view (so shadows can fade objects on their own) for (int32 i = 0; i < cluster->Instances.Count(); i++) { auto& instance = *cluster->Instances.Get()[i]; BoundingSphere sphere = instance.Bounds; - sphere.Center -= viewOrigin; - if (Float3::Distance(lodView->Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance && - renderContext.View.CullingFrustum.Intersects(sphere)) + sphere.Center -= context.ViewOrigin; + if (Float3::Distance(context.LodView.Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance && + context.RenderContext.View.CullingFrustum.Intersects(sphere) && + RenderTools::ComputeBoundsScreenRadiusSquared(sphere.Center, sphere.Radius, context.RenderContext.View) * context.ViewScreenSizeSq >= context.MinObjectPixelSizeSq) { const auto modelFrame = instance.DrawState.PrevFrame + 1; // Select a proper LOD index (model may be culled) - int32 lodIndex = RenderTools::ComputeModelLOD(model, sphere.Center, (float)sphere.Radius, renderContext); + int32 lodIndex = RenderTools::ComputeModelLOD(model, sphere.Center, (float)sphere.Radius, context.RenderContext); if (lodIndex == -1) { // Handling model fade-out transition @@ -231,20 +228,20 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, { 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, prevLOD, normalizedProgress, drawCallsLists, result); + DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result); } } else if (instance.DrawState.LODTransition < 255) { 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, prevLOD, normalizedProgress, drawCallsLists, result); + DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result); } } instance.DrawState.PrevFrame = frame; continue; } - lodIndex += renderContext.View.ModelLODBias; + lodIndex += context.RenderContext.View.ModelLODBias; lodIndex = model->ClampLODIndex(lodIndex); if (transitionLOD) @@ -278,19 +275,19 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, // Draw if (instance.DrawState.PrevLOD == lodIndex) { - DrawInstance(renderContext, instance, type, model, lodIndex, 0.0f, drawCallsLists, result); + DrawInstance(context, instance, model, lodIndex, 0.0f, drawCallsLists, result); } else if (instance.DrawState.PrevLOD == -1) { const float normalizedProgress = static_cast(instance.DrawState.LODTransition) * (1.0f / 255.0f); - DrawInstance(renderContext, instance, type, model, lodIndex, 1.0f - normalizedProgress, drawCallsLists, result); + DrawInstance(context, instance, model, lodIndex, 1.0f - normalizedProgress, drawCallsLists, result); } 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, prevLOD, normalizedProgress, drawCallsLists, result); - DrawInstance(renderContext, instance, type, model, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result); + DrawInstance(context, instance, model, prevLOD, normalizedProgress, drawCallsLists, result); + DrawInstance(context, instance, model, lodIndex, normalizedProgress - 1.0f, drawCallsLists, result); } //DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen); @@ -304,14 +301,11 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, #else -void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw) +void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, Mesh::DrawInfo& draw) { // Skip clusters that around too far from view - const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); - if (Float3::Distance(lodView->Position, cluster->TotalBoundsSphere.Center - lodView->Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) + if (Float3::Distance(context.LodView.Position, cluster->TotalBoundsSphere.Center - context.LodView.Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) return; - const Vector3 viewOrigin = renderContext.View.Origin; - //DebugDraw::DrawBox(cluster->Bounds, Color::Red); // Draw visible children @@ -323,10 +317,10 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, BoundingBox box; #define DRAW_CLUSTER(idx) \ box = cluster->Children[idx]->TotalBounds; \ - box.Minimum -= viewOrigin; \ - box.Maximum -= viewOrigin; \ - if (renderContext.View.CullingFrustum.Intersects(box)) \ - DrawCluster(renderContext, cluster->Children[idx], draw) + box.Minimum -= context.ViewOrigin; \ + box.Maximum -= context.ViewOrigin; \ + if (context.RenderContext.View.CullingFrustum.Intersects(box)) \ + DrawCluster(context, cluster->Children[idx], draw) DRAW_CLUSTER(0); DRAW_CLUSTER(1); DRAW_CLUSTER(2); @@ -342,16 +336,17 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, auto& instance = *cluster->Instances[i]; auto& type = FoliageTypes[instance.Type]; BoundingSphere sphere = instance.Bounds; - sphere.Center -= viewOrigin; + sphere.Center -= context.ViewOrigin; // Check if can draw this instance if (type._canDraw && - Float3::Distance(lodView->Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance && - renderContext.View.CullingFrustum.Intersects(sphere)) + Float3::Distance(context.LodView.Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance && + context.RenderContext.View.CullingFrustum.Intersects(sphere) && + RenderTools::ComputeBoundsScreenRadiusSquared(sphere.Center, sphere.Radius, context.RenderContext.View) * context.ViewScreenSizeSq >= context.MinObjectPixelSizeSq) { Matrix world; const Transform transform = _transform.LocalToWorld(instance.Transform); - const Float3 translation = transform.Translation - renderContext.View.Origin; + const Float3 translation = transform.Translation - context.ViewOrigin; Matrix::Transformation(transform.Scale, transform.Orientation, translation, world); // Disable motion blur @@ -367,7 +362,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, draw.PerInstanceRandom = instance.Random; draw.DrawModes = type.DrawModes; draw.SetStencilValue(_layer); - type.Model->Draw(renderContext, draw); + type.Model->Draw(context.RenderContext, draw); //DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen); @@ -446,22 +441,50 @@ void Foliage::DrawFoliageJob(int32 i) PROFILE_CPU(); PROFILE_MEM(Graphics); const FoliageType& type = FoliageTypes[i]; - if (type.IsReady() && type.Model->CanBeRendered()) + if (type._canDraw) { DrawCallsList drawCallsLists[MODEL_MAX_LODS]; for (RenderContext& renderContext : _renderContextBatch->Contexts) + { +#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING DrawType(renderContext, type, drawCallsLists); +#else + Mesh::DrawInfo draw; + draw.Flags = GetStaticFlags(); + draw.DrawModes = (DrawPass)(DrawPass::Default & renderContext.View.Pass); + draw.LODBias = 0; + draw.ForcedLOD = -1; + draw.VertexColors = nullptr; + draw.Deformation = nullptr; + DrawType(renderContext, type, draw); +#endif + } } } #endif +#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists) +#else +void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Mesh::DrawInfo& draw) +#endif { if (!type.Root || !FOLIAGE_CAN_DRAW(renderContext, type)) return; const DrawPass typeDrawModes = FOLIAGE_GET_DRAW_MODES(renderContext, type); PROFILE_CPU_ASSET(type.Model); + DrawContext context + { + renderContext, + renderContext.LodProxyView ? *renderContext.LodProxyView : renderContext.View, + type, + renderContext.View.Origin, + Math::Square(Graphics::Shadows::MinObjectPixelSize), + renderContext.View.ScreenSize.X * renderContext.View.ScreenSize.Y, + }; + if (context.RenderContext.View.Pass != DrawPass::Depth) + context.MinObjectPixelSizeSq = 0.0f; // Don't use it in main view #if FOLIAGE_USE_DRAW_CALLS_BATCHING // Initialize draw calls for foliage type all LODs meshes for (int32 lod = 0; lod < type.Model->LODs.Count(); lod++) @@ -506,7 +529,7 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Dr // Draw instances of the foliage type BatchedDrawCalls result(&renderContext.List->Memory); - DrawCluster(renderContext, type.Root, type, drawCallsLists, result); + DrawCluster(context, type.Root, drawCallsLists, result); // Submit draw calls with valid instances added for (auto& e : result) @@ -568,10 +591,22 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Dr } } #else - DrawCluster(renderContext, type.Root, draw); + DrawCluster(context, type.Root, draw); #endif } +void Foliage::InitType(const RenderView& view, FoliageType& type) +{ + const DrawPass drawModes = type.DrawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode); + type._canDraw = type.IsReady() && drawModes != DrawPass::None && type.Model && type.Model->CanBeRendered(); + for (int32 j = 0; j < type.Entries.Count(); j++) + { + auto& e = type.Entries[j]; + e.ReceiveDecals = type.ReceiveDecals != 0; + e.ShadowsMode = type.ShadowsMode; + } +} + int32 Foliage::GetInstancesCount() const { return Instances.Count(); @@ -1131,12 +1166,7 @@ void Foliage::Draw(RenderContext& renderContext) // Cache data per foliage instance type for (auto& type : FoliageTypes) { - for (int32 j = 0; j < type.Entries.Count(); j++) - { - auto& e = type.Entries[j]; - e.ReceiveDecals = type.ReceiveDecals != 0; - e.ShadowsMode = type.ShadowsMode; - } + InitType(renderContext.View, type); } if (renderContext.View.Pass == DrawPass::GlobalSDF) @@ -1202,12 +1232,7 @@ void Foliage::Draw(RenderContext& renderContext) // Draw single foliage instance projection into Global Surface Atlas auto& instance = *(FoliageInstance*)GlobalSurfaceAtlasPass::Instance()->GetCurrentActorObject(); auto& type = FoliageTypes[instance.Type]; - for (int32 i = 0; i < type.Entries.Count(); i++) - { - auto& e = type.Entries[i]; - e.ReceiveDecals = type.ReceiveDecals != 0; - e.ShadowsMode = type.ShadowsMode; - } + InitType(renderContext.View, type); Matrix world; const Transform transform = _transform.LocalToWorld(instance.Transform); renderContext.View.GetWorldMatrix(transform, world); @@ -1239,8 +1264,9 @@ void Foliage::Draw(RenderContext& renderContext) draw.LODBias = 0; draw.ForcedLOD = -1; draw.VertexColors = nullptr; + draw.Deformation = nullptr; #else - DrawCallsList drawCallsLists[MODEL_MAX_LODS]; + DrawCallsList draw[MODEL_MAX_LODS]; #endif #if FOLIAGE_USE_SINGLE_QUAD_TREE if (Root) @@ -1248,7 +1274,7 @@ void Foliage::Draw(RenderContext& renderContext) #else for (auto& type : FoliageTypes) { - DrawType(renderContext, type, drawCallsLists); + DrawType(renderContext, type, draw); } #endif } @@ -1265,14 +1291,7 @@ void Foliage::Draw(RenderContextBatch& renderContextBatch) { // Cache data per foliage instance type for (FoliageType& type : FoliageTypes) - { - for (int32 j = 0; j < type.Entries.Count(); j++) - { - auto& e = type.Entries[j]; - e.ReceiveDecals = type.ReceiveDecals != 0; - e.ShadowsMode = type.ShadowsMode; - } - } + InitType(view, type); // Run async job for each foliage type _renderContextBatch = &renderContextBatch; diff --git a/Source/Engine/Foliage/Foliage.h b/Source/Engine/Foliage/Foliage.h index 6f8b36cf4..83ab6206c 100644 --- a/Source/Engine/Foliage/Foliage.h +++ b/Source/Engine/Foliage/Foliage.h @@ -158,6 +158,15 @@ public: private: void AddToCluster(ChunkedArray& clusters, FoliageCluster* cluster, FoliageInstance& instance); + struct DrawContext + { + RenderContext& RenderContext; + const RenderView& LodView; + const FoliageType& FoliageType; + Vector3 ViewOrigin; + float MinObjectPixelSizeSq; + float ViewScreenSizeSq; + }; #if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING struct DrawKey { @@ -181,10 +190,12 @@ private: typedef Array> DrawCallsList; typedef Dictionary BatchedDrawCalls; - void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, const FoliageType& type, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; - void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; + void DrawInstance(DrawContext& context, FoliageInstance& instance, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; + void DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; + void DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists); #else - void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw); + void DrawCluster(DrawContext& context, FoliageCluster* cluster, Mesh::DrawInfo& draw); + void DrawType(RenderContext& renderContext, const FoliageType& type, Mesh::DrawInfo& draw); #endif #if !FOLIAGE_USE_SINGLE_QUAD_TREE void DrawClusterGlobalSDF(class GlobalSignDistanceFieldPass* globalSDF, const BoundingBox& globalSDFBounds, FoliageCluster* cluster, const FoliageType& type); @@ -192,7 +203,8 @@ private: void DrawFoliageJob(int32 i); RenderContextBatch* _renderContextBatch; #endif - void DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists); + + void InitType(const RenderView& view, FoliageType& type); public: /// diff --git a/Source/Engine/Foliage/FoliageType.h b/Source/Engine/Foliage/FoliageType.h index bbe7e7739..224ed0bd8 100644 --- a/Source/Engine/Foliage/FoliageType.h +++ b/Source/Engine/Foliage/FoliageType.h @@ -47,6 +47,7 @@ API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FoliageType : public ScriptingOb friend Foliage; private: uint8 _isReady : 1; + uint8 _canDraw : 1; public: /// diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index dd3344f5d..97af4f089 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -28,6 +28,7 @@ Quality Graphics::GIQuality = Quality::High; bool Graphics::GICascadesBlending = false; PostProcessSettings Graphics::PostProcessSettings; bool Graphics::SpreadWorkload = true; +float Graphics::Shadows::MinObjectPixelSize = 2.0f; bool Graphics::PostProcessing::ColorGradingVolumeLUT = true; #if GRAPHICS_API_NULL diff --git a/Source/Engine/Graphics/Graphics.h b/Source/Engine/Graphics/Graphics.h index d328431b0..571a7f086 100644 --- a/Source/Engine/Graphics/Graphics.h +++ b/Source/Engine/Graphics/Graphics.h @@ -85,8 +85,17 @@ public: API_FIELD() static bool SpreadWorkload; public: + // Shadows rendering configuration. + API_CLASS(Static, Attributes="DebugCommand") class FLAXENGINE_API Shadows + { + DECLARE_SCRIPTING_TYPE_MINIMAL(Shadows); + + // The minimum size in pixels of objects to cast shadows. Improves performance by skipping too small objects (eg. sub-pixel) from rendering into shadow maps. + API_FIELD() static float MinObjectPixelSize; + }; + // Post Processing effects rendering configuration. - API_CLASS(Static, Attributes = "DebugCommand") class FLAXENGINE_API PostProcessing + API_CLASS(Static, Attributes="DebugCommand") class FLAXENGINE_API PostProcessing { DECLARE_SCRIPTING_TYPE_MINIMAL(PostProcessing); diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index ac643b4e8..2bb206d6e 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -695,6 +695,7 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP DrawCallsLists[(int32)DrawCallsListType::MotionVectors].Indices.Add(index); } } + float minObjectPixelSizeSq = Math::Square(Graphics::Shadows::MinObjectPixelSize); for (int32 i = 1; i < renderContextBatch.Contexts.Count(); i++) { const RenderContext& renderContext = renderContextBatch.Contexts.Get()[i]; @@ -702,7 +703,8 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP drawModes = modes & renderContext.View.Pass; if (drawModes != DrawPass::None && (staticFlags & renderContext.View.StaticFlagsMask) == renderContext.View.StaticFlagsCompare && - renderContext.View.CullingFrustum.Intersects(bounds)) + renderContext.View.CullingFrustum.Intersects(bounds) && + RenderTools::ComputeBoundsScreenRadiusSquared(bounds.Center, bounds.Radius, renderContext.View) * (renderContext.View.ScreenSize.X * renderContext.View.ScreenSize.Y) >= minObjectPixelSizeSq) { renderContext.List->ShadowDepthDrawCallsList.Indices.Add(index); }