diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 2d218c531..c9e18ac9f 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -154,13 +154,16 @@ void Foliage::DrawInstance(DrawContext& context, FoliageInstance& instance, int3 // Add instance to the draw batch auto& instanceData = e->Instances.AddOne(); - Matrix world; - Transform transform; - _transform.LocalToWorld(instance.Transform, transform); - 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.LightmapUVsArea, drawCall.Surface.GeometrySize, instance.Random, worldDeterminantSign, lodDitherFactor); + if (!instance.CachedDrawWorldValid) + { + Transform transform; + _transform.LocalToWorld(instance.Transform, transform); + const Float3 translation = transform.Translation - context.ViewOrigin; + Matrix::Transformation(transform.Scale, transform.Orientation, translation, instance.CachedDrawWorld); + instance.CachedDrawWorldValid = true; + } + instanceData.Store(instance.CachedDrawWorld, instance.CachedDrawWorld, instance.LightmapUVsArea, drawCall.Surface.GeometrySize, instance.Random, worldDeterminantSign, lodDitherFactor); } } @@ -492,6 +495,7 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Me type.Model->GetLODsCount() - 1, renderContext.View.CullingFrustum, }; + _cachedDrawWorldOrigin = renderContext.View.Origin; if (context.RenderContext.View.Pass != DrawPass::Depth) context.MinObjectPixelSizeSq = 0.0f; // Don't use it in main view #if FOLIAGE_USE_DRAW_CALLS_BATCHING @@ -605,6 +609,21 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Me #endif } +void Foliage::PreDraw(const RenderView& view) +{ + // When origin changes, then update all instance matrices + if (_cachedDrawWorldOrigin != view.Origin) + { + _cachedDrawWorldOrigin = view.Origin; + for (auto i = Instances.Begin(); i.IsNotEnd(); ++i) + i->CachedDrawWorldValid = false; + } + + // Cache data per foliage instance type + for (FoliageType& type : FoliageTypes) + InitType(view, type); +} + void Foliage::InitType(const RenderView& view, FoliageType& type) { const DrawPass drawModes = type._drawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode); @@ -775,6 +794,7 @@ void Foliage::AddInstance(const FoliageInstance& instance) data->Bounds = BoundingSphere::Empty; data->Random = Random::Rand(); data->CullDistance = type->CullDistance + type->CullDistanceRandomRange * data->Random; + data->CachedDrawWorldValid = false; // Validate foliage type model if (!type->IsReady()) @@ -813,6 +833,7 @@ void Foliage::SetInstanceTransform(int32 index, const Transform& value) // Change transform instance.Transform = value; + instance.CachedDrawWorldValid = false; // Update bounds if (type.IsReady()) @@ -1200,12 +1221,7 @@ void Foliage::Draw(RenderContext& renderContext) return; PROFILE_CPU(); const RenderView& view = renderContext.View; - - // Cache data per foliage instance type - for (auto& type : FoliageTypes) - { - InitType(renderContext.View, type); - } + PreDraw(view); if (renderContext.View.Pass == DrawPass::GlobalSDF) { @@ -1305,8 +1321,6 @@ void Foliage::Draw(RenderContext& renderContext) draw.ForcedLOD = -1; draw.VertexColors = nullptr; draw.Deformation = nullptr; -#else - DrawCallsList draw[MODEL_MAX_LODS]; #endif #if FOLIAGE_USE_SINGLE_QUAD_TREE if (Root) @@ -1314,6 +1328,9 @@ void Foliage::Draw(RenderContext& renderContext) #else for (auto& type : FoliageTypes) { +#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING + DrawCallsList draw[MODEL_MAX_LODS]; +#endif DrawType(renderContext, type, draw); } #endif @@ -1329,9 +1346,7 @@ void Foliage::Draw(RenderContextBatch& renderContextBatch) const RenderView& view = renderContextBatch.GetMainContext().View; if (EnumHasAnyFlags(view.Pass, DrawPass::GBuffer) && !(view.Pass & (DrawPass::GlobalSDF | DrawPass::GlobalSurfaceAtlas)) && renderContextBatch.EnableAsync) { - // Cache data per foliage instance type - for (FoliageType& type : FoliageTypes) - InitType(view, type); + PreDraw(view); // Run async job for each foliage type _renderContextBatch = &renderContextBatch; @@ -1760,6 +1775,13 @@ void Foliage::OnTransformChanged() PROFILE_CPU(); + if (IsDuringPlay()) + { + // Invalidate cached world matrix + for (auto i = Instances.Begin(); i.IsNotEnd(); ++i) + i->CachedDrawWorldValid = false; + } + UpdateBounds(); RebuildClusters(); } diff --git a/Source/Engine/Foliage/Foliage.h b/Source/Engine/Foliage/Foliage.h index bef5f5231..948504736 100644 --- a/Source/Engine/Foliage/Foliage.h +++ b/Source/Engine/Foliage/Foliage.h @@ -20,6 +20,7 @@ API_CLASS() class FLAXENGINE_API Foliage final : public Actor private: bool _disableFoliageTypeEvents; int32 _sceneRenderingKey = -1; + Vector3 _cachedDrawWorldOrigin = Vector3::Zero; public: /// @@ -215,6 +216,7 @@ private: RenderContextBatch* _renderContextBatch; #endif + void PreDraw(const RenderView& view); void InitType(const RenderView& view, FoliageType& type); void UpdateBounds(); diff --git a/Source/Engine/Foliage/FoliageInstance.h b/Source/Engine/Foliage/FoliageInstance.h index e8173ebe5..10b08be3f 100644 --- a/Source/Engine/Foliage/FoliageInstance.h +++ b/Source/Engine/Foliage/FoliageInstance.h @@ -5,6 +5,7 @@ #include "Engine/Core/Math/Transform.h" #include "Engine/Core/Math/BoundingSphere.h" #include "Engine/Core/Math/Half.h" +#include "Engine/Core/Math/Matrix.h" /// /// Foliage instanced mesh instance. Packed data with very little of logic. Managed by the foliage chunks and foliage actor itself. @@ -48,6 +49,11 @@ API_STRUCT(NoPod, NoDefault) struct FLAXENGINE_API FoliageInstance /// byte DrawStateLODTransition = 255; + /// + /// Flag used to indicate whether CachedDrawWorld contains valid data. + /// + byte CachedDrawWorldValid = 0; + /// /// The per-instance random value from range [0;1]. /// @@ -63,6 +69,11 @@ API_STRUCT(NoPod, NoDefault) struct FLAXENGINE_API FoliageInstance /// Half4 LightmapUVsArea; + /// + /// Cached local-to-world transformation matrix for the instance used during rendering. Relative to the last rendering context origin. Valid only if CachedDrawWorldValid flag is set. + /// + Matrix CachedDrawWorld; + public: bool operator==(const FoliageInstance& v) const {