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
{