From 23f60da40df419b2105a482e1c2031438bfb72e3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Aug 2024 20:24:52 +0200 Subject: [PATCH 1/5] Fix various code issues found using PVS-Studio --- .../Animations/SceneAnimations/SceneAnimationPlayer.cpp | 2 +- Source/Engine/Core/Collections/Array.h | 7 +++++++ Source/Engine/Core/Collections/ChunkedArray.h | 8 ++++++++ Source/Engine/Core/Collections/Dictionary.h | 7 +++++++ Source/Engine/Core/Collections/HashSet.h | 7 +++++++ Source/Engine/Core/Types/DataContainer.h | 2 +- Source/Engine/Core/Types/StringView.h | 6 ++++++ Source/Engine/Particles/Particles.cpp | 6 +++--- Source/Engine/Render2D/Render2D.cpp | 2 +- Source/Engine/Scripting/ScriptingType.h | 6 ++++++ Source/Engine/Terrain/TerrainPatch.cpp | 3 ++- 11 files changed, 49 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.cpp b/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.cpp index ce23b92a4..a06150875 100644 --- a/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.cpp +++ b/Source/Engine/Animations/SceneAnimations/SceneAnimationPlayer.cpp @@ -92,7 +92,7 @@ void SceneAnimationPlayer::Pause() if (_state != PlayState::Playing) return; - if (IsActiveInHierarchy() && _state == PlayState::Playing) + if (IsActiveInHierarchy()) { UNREGISTER_TICK; } diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index 7281fa8d8..a17d8f686 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -1027,6 +1027,13 @@ public: return *this; } + Iterator& operator=(Iterator&& v) + { + _array = v._array; + _index = v._index; + return *this; + } + Iterator& operator++() { if (_index != _array->_count) diff --git a/Source/Engine/Core/Collections/ChunkedArray.h b/Source/Engine/Core/Collections/ChunkedArray.h index 157efcd0f..457778b26 100644 --- a/Source/Engine/Core/Collections/ChunkedArray.h +++ b/Source/Engine/Core/Collections/ChunkedArray.h @@ -173,6 +173,14 @@ public: return *this; } + Iterator& operator=(Iterator&& v) + { + _collection = v._collection; + _chunkIndex = v._chunkIndex; + _index = v._index; + return *this; + } + Iterator& operator++() { // Check if it is not at end diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index 7c180e56e..9e516dae7 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -348,6 +348,13 @@ public: return *this; } + Iterator& operator=(Iterator&& v) + { + _collection = v._collection; + _index = v._index; + return *this; + } + Iterator& operator++() { const int32 capacity = _collection->_size; diff --git a/Source/Engine/Core/Collections/HashSet.h b/Source/Engine/Core/Collections/HashSet.h index b76244860..4a310fef0 100644 --- a/Source/Engine/Core/Collections/HashSet.h +++ b/Source/Engine/Core/Collections/HashSet.h @@ -329,6 +329,13 @@ public: return *this; } + Iterator& operator=(Iterator&& v) + { + _collection = v._collection; + _index = v._index; + return *this; + } + Iterator& operator++() { const int32 capacity = _collection->_size; diff --git a/Source/Engine/Core/Types/DataContainer.h b/Source/Engine/Core/Types/DataContainer.h index 7893eb1ca..9d506b1eb 100644 --- a/Source/Engine/Core/Types/DataContainer.h +++ b/Source/Engine/Core/Types/DataContainer.h @@ -338,7 +338,7 @@ public: Platform::MemoryCopy(Base::_data, prev, prevLength * sizeof(T)); Platform::MemoryCopy(Base::_data + prevLength * sizeof(T), data, length * sizeof(T)); - if (_isAllocated && prev) + if (_isAllocated) Allocator::Free(prev); _isAllocated = true; } diff --git a/Source/Engine/Core/Types/StringView.h b/Source/Engine/Core/Types/StringView.h index 052bc1d54..365b2fa7f 100644 --- a/Source/Engine/Core/Types/StringView.h +++ b/Source/Engine/Core/Types/StringView.h @@ -28,6 +28,12 @@ protected: { } + StringViewBase(const StringViewBase& other) + : _data(other._data) + , _length(other._length) + { + } + public: typedef T CharType; diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 66fc91cce..53c1ab4b1 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -289,9 +289,9 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa // Check if need to setup ribbon modules int32 ribbonModuleIndex = 0; int32 ribbonModulesDrawIndicesPos = 0; - int32 ribbonModulesDrawIndicesStart[PARTICLE_EMITTER_MAX_RIBBONS]; - int32 ribbonModulesDrawIndicesCount[PARTICLE_EMITTER_MAX_RIBBONS]; - int32 ribbonModulesSegmentCount[PARTICLE_EMITTER_MAX_RIBBONS]; + int32 ribbonModulesDrawIndicesStart[PARTICLE_EMITTER_MAX_RIBBONS] = {}; + int32 ribbonModulesDrawIndicesCount[PARTICLE_EMITTER_MAX_RIBBONS] = {}; + int32 ribbonModulesSegmentCount[PARTICLE_EMITTER_MAX_RIBBONS] = {}; if (emitter->Graph.RibbonRenderingModules.HasItems()) { // Prepare ribbon data diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 84de6666d..3c3b5a106 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -481,7 +481,7 @@ bool CachedPSO::Init(GPUShader* shader, bool useDepth) // Create pipeline states GPUPipelineState::Description desc = GPUPipelineState::Description::DefaultFullscreenTriangle; - desc.DepthEnable = desc.DepthWriteEnable = useDepth; + desc.DepthEnable = useDepth; desc.DepthWriteEnable = false; desc.DepthClipEnable = false; desc.VS = shader->GetVS("VS"); diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index 17a844097..6294ecbd9 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -40,6 +40,12 @@ struct FLAXENGINE_API ScriptingTypeHandle { } + FORCE_INLINE ScriptingTypeHandle(ScriptingTypeHandle&& other) + : Module(other.Module) + , TypeIndex(other.TypeIndex) + { + } + ScriptingTypeHandle(const ScriptingTypeInitializer& initializer); FORCE_INLINE operator bool() const diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 93269269a..10ce5ba6a 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -1215,7 +1215,8 @@ Color32* TerrainPatch::GetSplatMapData(int32 index) void TerrainPatch::ClearSplatMapCache() { PROFILE_CPU_NAMED("Terrain.ClearSplatMapCache"); - _cachedSplatMap->Clear(); + if (_cachedSplatMap) + _cachedSplatMap->Clear(); } void TerrainPatch::ClearCache() From b8cb1a828a4f089eaf807ac5d13577295b5a30a3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Aug 2024 22:29:33 +0200 Subject: [PATCH 2/5] Fix foliage shadows rendering when using cull distance #2489 --- Source/Engine/Foliage/Foliage.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 39451207d..bb9459677 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -137,9 +137,10 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const { // Skip clusters that around too far from view - const Vector3 viewOrigin = renderContext.View.Origin; - if (Float3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center - viewOrigin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) + 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) return; + const Vector3 viewOrigin = renderContext.View.Origin; //DebugDraw::DrawBox(cluster->Bounds, Color::Red); @@ -172,7 +173,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, auto& instance = *cluster->Instances.Get()[i]; BoundingSphere sphere = instance.Bounds; sphere.Center -= viewOrigin; - if (Float3::Distance(renderContext.View.Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance && + if (Float3::Distance(lodView->Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance && renderContext.View.CullingFrustum.Intersects(sphere)) { const auto modelFrame = instance.DrawState.PrevFrame + 1; @@ -266,9 +267,10 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw) { // Skip clusters that around too far from view - const Vector3 viewOrigin = renderContext.View.Origin; - if (Float3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center - viewOrigin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) + 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) return; + const Vector3 viewOrigin = renderContext.View.Origin; //DebugDraw::DrawBox(cluster->Bounds, Color::Red); @@ -304,7 +306,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, // Check if can draw this instance if (type._canDraw && - Float3::Distance(renderContext.View.Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance && + Float3::Distance(lodView->Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance && renderContext.View.CullingFrustum.Intersects(sphere)) { Matrix world; From fc66738dca3edbbc55f0069c70e1965f27aa3b35 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Aug 2024 23:29:42 +0200 Subject: [PATCH 3/5] Add automatic heightmap files removal on Editor shutdown for deleted terrains #1902 --- .../Editor/Managed/ManagedEditor.Internal.cpp | 10 +++ Source/Editor/Managed/ManagedEditor.h | 7 ++ .../Editor/SceneGraph/Actors/TerrainNode.cs | 76 +++++++++++++++++++ Source/Engine/Terrain/TerrainPatch.cpp | 5 ++ Source/Engine/Terrain/TerrainPatch.h | 6 ++ 5 files changed, 104 insertions(+) diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index b2867c2df..31295253f 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -602,3 +602,13 @@ bool ManagedEditor::CreateAsset(const String& tag, String outputPath) FileSystem::NormalizePath(outputPath); return AssetsImportingManager::Create(tag, outputPath); } + +Array ManagedEditor::GetAssetReferences(const Guid& assetId) +{ + Array result; + if (auto* asset = Content::Load(assetId)) + { + asset->GetReferences(result); + } + return result; +} diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h index 5c7907d17..eb1cceef8 100644 --- a/Source/Editor/Managed/ManagedEditor.h +++ b/Source/Editor/Managed/ManagedEditor.h @@ -218,6 +218,13 @@ public: /// Output asset path. API_FUNCTION() static bool CreateAsset(const String& tag, String outputPath); + /// + /// Gets a list of asset references of a given asset. + /// + /// The asset ID. + /// List of referenced assets. + API_FUNCTION() static Array GetAssetReferences(const Guid& assetId); + public: API_STRUCT(Internal, NoDefault) struct VisualScriptStackFrame { diff --git a/Source/Editor/SceneGraph/Actors/TerrainNode.cs b/Source/Editor/SceneGraph/Actors/TerrainNode.cs index 1f95981bd..099dd5347 100644 --- a/Source/Editor/SceneGraph/Actors/TerrainNode.cs +++ b/Source/Editor/SceneGraph/Actors/TerrainNode.cs @@ -1,5 +1,9 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using FlaxEngine; namespace FlaxEditor.SceneGraph.Actors @@ -10,6 +14,15 @@ namespace FlaxEditor.SceneGraph.Actors [HideInEditor] public sealed class TerrainNode : ActorNode { + private struct RemovedTerrain + { + public Guid SceneId; + public Guid TerrainId; + public string[] Files; + } + + private static List _cleanupFiles; + /// public TerrainNode(Actor actor) : base(actor) @@ -18,5 +31,68 @@ namespace FlaxEditor.SceneGraph.Actors /// public override bool AffectsNavigation => true; + + /// + public override void Delete() + { + // Schedule terrain data files for automatic cleanup + if (_cleanupFiles == null) + { + _cleanupFiles = new List(); + Engine.RequestingExit += OnRequestingExit; + } + if (Actor is Terrain terrain) + { + var removed = new RemovedTerrain + { + SceneId = terrain.Scene?.ID ?? Guid.Empty, + TerrainId = terrain.ID, + Files = new string[4], + }; + for (int i = 0; i < terrain.PatchesCount; i++) + { + var patch = terrain.GetPatch(i); + if (patch.Heightmap) + removed.Files[0] = patch.Heightmap.Path; + if (patch.Heightfield) + removed.Files[1] = patch.Heightfield.Path; + if (patch.GetSplatmap(0)) + removed.Files[2] = patch.GetSplatmap(0).Path; + if (patch.GetSplatmap(1)) + removed.Files[3] = patch.GetSplatmap(1).Path; + } + _cleanupFiles.Add(removed); + } + + base.Delete(); + } + + void OnRequestingExit() + { + foreach (var e in _cleanupFiles) + { + try + { + // Skip removing this terrain file sif it's still referenced + var sceneReferences = Editor.GetAssetReferences(e.SceneId); + if (sceneReferences != null && sceneReferences.Contains(e.TerrainId)) + continue; + + // Delete files + foreach (var file in e.Files) + { + if (file != null && File.Exists(file)) + File.Delete(file); + } + } + catch (Exception ex) + { + Editor.LogWarning(ex); + } + } + _cleanupFiles.Clear(); + _cleanupFiles = null; + Engine.RequestingExit -= OnRequestingExit; + } } } diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 10ce5ba6a..592c2e7f9 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -113,6 +113,11 @@ TerrainPatch::~TerrainPatch() #endif } +RawDataAsset* TerrainPatch::GetHeightfield() const +{ + return _heightfield.Get(); +} + void TerrainPatch::RemoveLightmap() { for (auto& chunk : Chunks) diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h index 295f37144..05a7693fe 100644 --- a/Source/Engine/Terrain/TerrainPatch.h +++ b/Source/Engine/Terrain/TerrainPatch.h @@ -149,6 +149,12 @@ public: return GetChunk(z * Terrain::ChunksCountEdge + x); } + /// + /// Gets the heightfield collision data asset. + /// + /// The heightfield data asset. + API_PROPERTY() RawDataAsset* GetHeightfield() const; + /// /// Gets the splatmap assigned to this patch. /// From ef540bc49892b00aaec10f5437d5ada0a2a2ec07 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Jul 2024 17:14:51 +0200 Subject: [PATCH 4/5] Fix crash when drawing particle effect where one of the emitter assets is not yet loaded --- Source/Engine/Particles/Particles.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 53c1ab4b1..262b74ca9 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -931,6 +931,8 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe if (!buffer || (buffer->Mode == ParticlesSimulationMode::CPU && buffer->CPU.Count == 0)) continue; auto emitter = buffer->Emitter; + if (!emitter || !emitter->IsLoaded()) + continue; buffer->Emitter->GraphExecutorCPU.Draw(buffer->Emitter, effect, emitterData, renderContext, worlds[(int32)emitter->SimulationSpace]); } @@ -949,6 +951,8 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe if (!buffer) continue; auto emitter = buffer->Emitter; + if (!emitter || !emitter->IsLoaded()) + continue; drawCall.World = worlds[(int32)emitter->SimulationSpace]; drawCall.WorldDeterminantSign = worldDeterminantSigns[(int32)emitter->SimulationSpace]; From e5f0e05d43f3359cd5fdf1eaaab7096b93ca6103 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 23 Aug 2024 00:00:42 +0200 Subject: [PATCH 5/5] Fix terrain rendering to use the same LOD for main view and shadow passes #2555 #2256 --- .../CustomEditors/Dedicated/TerrainEditor.cs | 5 +- Source/Engine/Terrain/Terrain.cpp | 82 +++++++++++++------ Source/Engine/Terrain/Terrain.h | 4 +- Source/Engine/Terrain/TerrainChunk.h | 6 +- 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/TerrainEditor.cs b/Source/Editor/CustomEditors/Dedicated/TerrainEditor.cs index 30ffb6866..8aaf5b7d6 100644 --- a/Source/Editor/CustomEditors/Dedicated/TerrainEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/TerrainEditor.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using FlaxEditor.Utilities; using FlaxEngine; namespace FlaxEditor.CustomEditors.Dedicated @@ -29,8 +30,8 @@ namespace FlaxEditor.CustomEditors.Dedicated chunkSize, 1.0f / (resolution.X + 1e-9f), 1.0f / (resolution.Z + 1e-9f), - totalSize.X * 0.00001f, - totalSize.Z * 0.00001f + totalSize.X / Units.Meters2Units * 0.001f, + totalSize.Z / Units.Meters2Units * 0.001f ); var label = layout.Label(text); label.Label.AutoHeight = true; diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index 66ecbd377..d0b9e532f 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -291,7 +291,7 @@ void Terrain::SetPhysicalMaterials(const Array drawnChunks; + for (RenderContext& renderContext : renderContextBatch.Contexts) + { + const DrawPass drawModes = DrawModes & renderContext.View.Pass; + if (drawModes == DrawPass::None) + continue; + DrawImpl(renderContext, drawnChunks); + } +} + void Terrain::Draw(RenderContext& renderContext) { const DrawPass drawModes = DrawModes & renderContext.View.Pass; if (drawModes == DrawPass::None) return; PROFILE_CPU(); - if (renderContext.View.Pass == DrawPass::GlobalSDF) + if (DrawSetup(renderContext)) + return; + HashSet drawnChunks; + DrawImpl(renderContext, drawnChunks); +} + +bool Terrain::DrawSetup(RenderContext& renderContext) +{ + // Special drawing modes + const DrawPass drawModes = DrawModes & renderContext.View.Pass; + if (drawModes == DrawPass::GlobalSDF) { - if ((DrawModes & DrawPass::GlobalSDF) == DrawPass::None) - return; const float chunkSize = TERRAIN_UNITS_PER_VERTEX * (float)_chunkSize; const float posToUV = 0.25f / chunkSize; Float4 localToUV(posToUV, posToUV, 0.0f, 0.0f); @@ -533,12 +556,10 @@ void Terrain::Draw(RenderContext& renderContext) patchTransform = _transform.LocalToWorld(patchTransform); GlobalSignDistanceFieldPass::Instance()->RasterizeHeightfield(this, patch->Heightmap->GetTexture(), patchTransform, patch->_bounds, localToUV); } - return; + return true; } - if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas) + if (drawModes == DrawPass::GlobalSurfaceAtlas) { - if ((DrawModes & DrawPass::GlobalSurfaceAtlas) == DrawPass::None) - return; for (TerrainPatch* patch : _patches) { if (!patch->Heightmap) @@ -556,11 +577,27 @@ void Terrain::Draw(RenderContext& renderContext) GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, chunk, chunkSphere, chunk->GetTransform(), localBounds, 1 << 2, false); } } - return; + return true; } + // Reset cached LOD for chunks (prevent LOD transition from invisible chunks) + for (int32 patchIndex = 0; patchIndex < _patches.Count(); patchIndex++) + { + const auto patch = _patches[patchIndex]; + for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++) + { + auto chunk = &patch->Chunks[chunkIndex]; + chunk->_cachedDrawLOD = 0; + } + } + + return false; +} + +void Terrain::DrawImpl(RenderContext& renderContext, HashSet& drawnChunks) +{ // Collect chunks to render and calculate LOD/material for them (required to be done before to gather NeighborLOD) - _drawChunks.Clear(); + Array drawChunks; // Frustum vs Box culling for patches const BoundingFrustum frustum = renderContext.View.CullingFrustum; @@ -579,33 +616,24 @@ void Terrain::Draw(RenderContext& renderContext) for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++) { auto chunk = &patch->Chunks[chunkIndex]; - chunk->_cachedDrawLOD = 0; bounds = BoundingBox(chunk->_bounds.Minimum - origin, chunk->_bounds.Maximum - origin); if (renderContext.View.IsCullingDisabled || frustum.Intersects(bounds)) { - if (chunk->PrepareDraw(renderContext)) - { - // Add chunk for drawing - _drawChunks.Add(chunk); - } + if (!drawnChunks.Contains(chunk) && !chunk->PrepareDraw(renderContext)) + continue; + + // Add chunk for drawing + drawChunks.Add(chunk); + drawnChunks.Add(chunk); } } } - else - { - // Reset cached LOD for chunks (prevent LOD transition from invisible chunks) - for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++) - { - auto chunk = &patch->Chunks[chunkIndex]; - chunk->_cachedDrawLOD = 0; - } - } } // Draw all visible chunks - for (int32 i = 0; i < _drawChunks.Count(); i++) + for (int32 i = 0; i < drawChunks.Count(); i++) { - _drawChunks.Get()[i]->Draw(renderContext); + drawChunks.Get()[i]->Draw(renderContext); } } diff --git a/Source/Engine/Terrain/Terrain.h b/Source/Engine/Terrain/Terrain.h index 17f4bdccd..d9ef89fb5 100644 --- a/Source/Engine/Terrain/Terrain.h +++ b/Source/Engine/Terrain/Terrain.h @@ -72,7 +72,6 @@ private: Vector3 _boundsExtent; Float3 _cachedScale; Array> _patches; - Array _drawChunks; Array, FixedAllocation<8>> _physicalMaterials; public: @@ -424,9 +423,12 @@ private: #if TERRAIN_USE_PHYSICS_DEBUG void DrawPhysicsDebug(RenderView& view); #endif + bool DrawSetup(RenderContext& renderContext); + void DrawImpl(RenderContext& renderContext, HashSet& drawnChunks); public: // [PhysicsColliderActor] + void Draw(RenderContextBatch& renderContextBatch) override; void Draw(RenderContext& renderContext) override; #if USE_EDITOR void OnDebugDrawSelected() override; diff --git a/Source/Engine/Terrain/TerrainChunk.h b/Source/Engine/Terrain/TerrainChunk.h index f73303ae1..b4d70ec6d 100644 --- a/Source/Engine/Terrain/TerrainChunk.h +++ b/Source/Engine/Terrain/TerrainChunk.h @@ -34,9 +34,9 @@ private: float _perInstanceRandom; float _yOffset, _yHeight; - TerrainChunk* _neighbors[4]; - byte _cachedDrawLOD; - IMaterial* _cachedDrawMaterial; + TerrainChunk* _neighbors[4] = {}; + byte _cachedDrawLOD = 0; + IMaterial* _cachedDrawMaterial = nullptr; void Init(TerrainPatch* patch, uint16 x, uint16 z);