Merge remote-tracking branch 'origin/master' into 1.9

This commit is contained in:
Wojtek Figat
2024-08-23 00:05:00 +02:00
20 changed files with 225 additions and 46 deletions

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using FlaxEditor.Utilities;
using FlaxEngine; using FlaxEngine;
namespace FlaxEditor.CustomEditors.Dedicated namespace FlaxEditor.CustomEditors.Dedicated
@@ -29,8 +30,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
chunkSize, chunkSize,
1.0f / (resolution.X + 1e-9f), 1.0f / (resolution.X + 1e-9f),
1.0f / (resolution.Z + 1e-9f), 1.0f / (resolution.Z + 1e-9f),
totalSize.X * 0.00001f, totalSize.X / Units.Meters2Units * 0.001f,
totalSize.Z * 0.00001f totalSize.Z / Units.Meters2Units * 0.001f
); );
var label = layout.Label(text); var label = layout.Label(text);
label.Label.AutoHeight = true; label.Label.AutoHeight = true;

View File

@@ -602,3 +602,13 @@ bool ManagedEditor::CreateAsset(const String& tag, String outputPath)
FileSystem::NormalizePath(outputPath); FileSystem::NormalizePath(outputPath);
return AssetsImportingManager::Create(tag, outputPath); return AssetsImportingManager::Create(tag, outputPath);
} }
Array<Guid> ManagedEditor::GetAssetReferences(const Guid& assetId)
{
Array<Guid> result;
if (auto* asset = Content::Load<Asset>(assetId))
{
asset->GetReferences(result);
}
return result;
}

View File

@@ -218,6 +218,13 @@ public:
/// <param name="outputPath">Output asset path.</param> /// <param name="outputPath">Output asset path.</param>
API_FUNCTION() static bool CreateAsset(const String& tag, String outputPath); API_FUNCTION() static bool CreateAsset(const String& tag, String outputPath);
/// <summary>
/// Gets a list of asset references of a given asset.
/// </summary>
/// <param name="assetId">The asset ID.</param>
/// <returns>List of referenced assets.</returns>
API_FUNCTION() static Array<Guid> GetAssetReferences(const Guid& assetId);
public: public:
API_STRUCT(Internal, NoDefault) struct VisualScriptStackFrame API_STRUCT(Internal, NoDefault) struct VisualScriptStackFrame
{ {

View File

@@ -1,5 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FlaxEngine; using FlaxEngine;
namespace FlaxEditor.SceneGraph.Actors namespace FlaxEditor.SceneGraph.Actors
@@ -10,6 +14,15 @@ namespace FlaxEditor.SceneGraph.Actors
[HideInEditor] [HideInEditor]
public sealed class TerrainNode : ActorNode public sealed class TerrainNode : ActorNode
{ {
private struct RemovedTerrain
{
public Guid SceneId;
public Guid TerrainId;
public string[] Files;
}
private static List<RemovedTerrain> _cleanupFiles;
/// <inheritdoc /> /// <inheritdoc />
public TerrainNode(Actor actor) public TerrainNode(Actor actor)
: base(actor) : base(actor)
@@ -18,5 +31,68 @@ namespace FlaxEditor.SceneGraph.Actors
/// <inheritdoc /> /// <inheritdoc />
public override bool AffectsNavigation => true; public override bool AffectsNavigation => true;
/// <inheritdoc />
public override void Delete()
{
// Schedule terrain data files for automatic cleanup
if (_cleanupFiles == null)
{
_cleanupFiles = new List<RemovedTerrain>();
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;
}
} }
} }

View File

@@ -92,7 +92,7 @@ void SceneAnimationPlayer::Pause()
if (_state != PlayState::Playing) if (_state != PlayState::Playing)
return; return;
if (IsActiveInHierarchy() && _state == PlayState::Playing) if (IsActiveInHierarchy())
{ {
UNREGISTER_TICK; UNREGISTER_TICK;
} }

View File

@@ -1027,6 +1027,13 @@ public:
return *this; return *this;
} }
Iterator& operator=(Iterator&& v)
{
_array = v._array;
_index = v._index;
return *this;
}
Iterator& operator++() Iterator& operator++()
{ {
if (_index != _array->_count) if (_index != _array->_count)

View File

@@ -173,6 +173,14 @@ public:
return *this; return *this;
} }
Iterator& operator=(Iterator&& v)
{
_collection = v._collection;
_chunkIndex = v._chunkIndex;
_index = v._index;
return *this;
}
Iterator& operator++() Iterator& operator++()
{ {
// Check if it is not at end // Check if it is not at end

View File

@@ -348,6 +348,13 @@ public:
return *this; return *this;
} }
Iterator& operator=(Iterator&& v)
{
_collection = v._collection;
_index = v._index;
return *this;
}
Iterator& operator++() Iterator& operator++()
{ {
const int32 capacity = _collection->_size; const int32 capacity = _collection->_size;

View File

@@ -329,6 +329,13 @@ public:
return *this; return *this;
} }
Iterator& operator=(Iterator&& v)
{
_collection = v._collection;
_index = v._index;
return *this;
}
Iterator& operator++() Iterator& operator++()
{ {
const int32 capacity = _collection->_size; const int32 capacity = _collection->_size;

View File

@@ -338,7 +338,7 @@ public:
Platform::MemoryCopy(Base::_data, prev, prevLength * sizeof(T)); Platform::MemoryCopy(Base::_data, prev, prevLength * sizeof(T));
Platform::MemoryCopy(Base::_data + prevLength * sizeof(T), data, length * sizeof(T)); Platform::MemoryCopy(Base::_data + prevLength * sizeof(T), data, length * sizeof(T));
if (_isAllocated && prev) if (_isAllocated)
Allocator::Free(prev); Allocator::Free(prev);
_isAllocated = true; _isAllocated = true;
} }

View File

@@ -28,6 +28,12 @@ protected:
{ {
} }
StringViewBase(const StringViewBase& other)
: _data(other._data)
, _length(other._length)
{
}
public: public:
typedef T CharType; typedef T CharType;

View File

@@ -133,9 +133,10 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const
{ {
// Skip clusters that around too far from view // Skip clusters that around too far from view
const Vector3 viewOrigin = renderContext.View.Origin; const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View);
if (Float3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center - viewOrigin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) if (Float3::Distance(lodView->Position, cluster->TotalBoundsSphere.Center - lodView->Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
return; return;
const Vector3 viewOrigin = renderContext.View.Origin;
//DebugDraw::DrawBox(cluster->Bounds, Color::Red); //DebugDraw::DrawBox(cluster->Bounds, Color::Red);
@@ -168,7 +169,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
auto& instance = *cluster->Instances.Get()[i]; auto& instance = *cluster->Instances.Get()[i];
BoundingSphere sphere = instance.Bounds; BoundingSphere sphere = instance.Bounds;
sphere.Center -= viewOrigin; 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)) renderContext.View.CullingFrustum.Intersects(sphere))
{ {
const auto modelFrame = instance.DrawState.PrevFrame + 1; const auto modelFrame = instance.DrawState.PrevFrame + 1;
@@ -262,9 +263,10 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw) void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw)
{ {
// Skip clusters that around too far from view // Skip clusters that around too far from view
const Vector3 viewOrigin = renderContext.View.Origin; const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View);
if (Float3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center - viewOrigin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance) if (Float3::Distance(lodView->Position, cluster->TotalBoundsSphere.Center - lodView->Origin) - (float)cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
return; return;
const Vector3 viewOrigin = renderContext.View.Origin;
//DebugDraw::DrawBox(cluster->Bounds, Color::Red); //DebugDraw::DrawBox(cluster->Bounds, Color::Red);
@@ -300,7 +302,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
// Check if can draw this instance // Check if can draw this instance
if (type._canDraw && 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)) renderContext.View.CullingFrustum.Intersects(sphere))
{ {
Matrix world; Matrix world;

View File

@@ -289,9 +289,9 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Check if need to setup ribbon modules // Check if need to setup ribbon modules
int32 ribbonModuleIndex = 0; int32 ribbonModuleIndex = 0;
int32 ribbonModulesDrawIndicesPos = 0; int32 ribbonModulesDrawIndicesPos = 0;
int32 ribbonModulesDrawIndicesStart[PARTICLE_EMITTER_MAX_RIBBONS]; int32 ribbonModulesDrawIndicesStart[PARTICLE_EMITTER_MAX_RIBBONS] = {};
int32 ribbonModulesDrawIndicesCount[PARTICLE_EMITTER_MAX_RIBBONS]; int32 ribbonModulesDrawIndicesCount[PARTICLE_EMITTER_MAX_RIBBONS] = {};
int32 ribbonModulesSegmentCount[PARTICLE_EMITTER_MAX_RIBBONS]; int32 ribbonModulesSegmentCount[PARTICLE_EMITTER_MAX_RIBBONS] = {};
if (emitter->Graph.RibbonRenderingModules.HasItems()) if (emitter->Graph.RibbonRenderingModules.HasItems())
{ {
// Prepare ribbon data // Prepare ribbon data

View File

@@ -481,7 +481,7 @@ bool CachedPSO::Init(GPUShader* shader, bool useDepth)
// Create pipeline states // Create pipeline states
GPUPipelineState::Description desc = GPUPipelineState::Description::DefaultFullscreenTriangle; GPUPipelineState::Description desc = GPUPipelineState::Description::DefaultFullscreenTriangle;
desc.DepthEnable = desc.DepthWriteEnable = useDepth; desc.DepthEnable = useDepth;
desc.DepthWriteEnable = false; desc.DepthWriteEnable = false;
desc.DepthClipEnable = false; desc.DepthClipEnable = false;
desc.VS = shader->GetVS("VS"); desc.VS = shader->GetVS("VS");

View File

@@ -40,6 +40,12 @@ struct FLAXENGINE_API ScriptingTypeHandle
{ {
} }
FORCE_INLINE ScriptingTypeHandle(ScriptingTypeHandle&& other)
: Module(other.Module)
, TypeIndex(other.TypeIndex)
{
}
ScriptingTypeHandle(const ScriptingTypeInitializer& initializer); ScriptingTypeHandle(const ScriptingTypeInitializer& initializer);
FORCE_INLINE operator bool() const FORCE_INLINE operator bool() const

View File

@@ -291,7 +291,7 @@ void Terrain::SetPhysicalMaterials(const Array<JsonAssetReference<PhysicalMateri
_physicalMaterials = value; _physicalMaterials = value;
_physicalMaterials.Resize(8); _physicalMaterials.Resize(8);
JsonAsset* materials[8]; JsonAsset* materials[8];
for (int32 i = 0;i<8;i++) for (int32 i = 0; i < 8; i++)
materials[i] = _physicalMaterials[i]; materials[i] = _physicalMaterials[i];
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++) for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{ {
@@ -509,16 +509,39 @@ void Terrain::RemovePatch(const Int2& patchCoord)
#endif #endif
void Terrain::Draw(RenderContextBatch& renderContextBatch)
{
PROFILE_CPU();
if (DrawSetup(renderContextBatch.GetMainContext()))
return;
HashSet<TerrainChunk*, RendererAllocation> 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) void Terrain::Draw(RenderContext& renderContext)
{ {
const DrawPass drawModes = DrawModes & renderContext.View.Pass; const DrawPass drawModes = DrawModes & renderContext.View.Pass;
if (drawModes == DrawPass::None) if (drawModes == DrawPass::None)
return; return;
PROFILE_CPU(); PROFILE_CPU();
if (renderContext.View.Pass == DrawPass::GlobalSDF) if (DrawSetup(renderContext))
return;
HashSet<TerrainChunk*, RendererAllocation> 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 chunkSize = TERRAIN_UNITS_PER_VERTEX * (float)_chunkSize;
const float posToUV = 0.25f / chunkSize; const float posToUV = 0.25f / chunkSize;
Float4 localToUV(posToUV, posToUV, 0.0f, 0.0f); Float4 localToUV(posToUV, posToUV, 0.0f, 0.0f);
@@ -533,12 +556,10 @@ void Terrain::Draw(RenderContext& renderContext)
patchTransform = _transform.LocalToWorld(patchTransform); patchTransform = _transform.LocalToWorld(patchTransform);
GlobalSignDistanceFieldPass::Instance()->RasterizeHeightfield(this, patch->Heightmap->GetTexture(), patchTransform, patch->_bounds, localToUV); 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) for (TerrainPatch* patch : _patches)
{ {
if (!patch->Heightmap) if (!patch->Heightmap)
@@ -556,11 +577,27 @@ void Terrain::Draw(RenderContext& renderContext)
GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, chunk, chunkSphere, chunk->GetTransform(), localBounds, 1 << 2, false); 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<TerrainChunk*, RendererAllocation>& drawnChunks)
{
// Collect chunks to render and calculate LOD/material for them (required to be done before to gather NeighborLOD) // Collect chunks to render and calculate LOD/material for them (required to be done before to gather NeighborLOD)
_drawChunks.Clear(); Array<TerrainChunk*, RendererAllocation> drawChunks;
// Frustum vs Box culling for patches // Frustum vs Box culling for patches
const BoundingFrustum frustum = renderContext.View.CullingFrustum; const BoundingFrustum frustum = renderContext.View.CullingFrustum;
@@ -579,33 +616,24 @@ void Terrain::Draw(RenderContext& renderContext)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++) for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{ {
auto chunk = &patch->Chunks[chunkIndex]; auto chunk = &patch->Chunks[chunkIndex];
chunk->_cachedDrawLOD = 0;
bounds = BoundingBox(chunk->_bounds.Minimum - origin, chunk->_bounds.Maximum - origin); bounds = BoundingBox(chunk->_bounds.Minimum - origin, chunk->_bounds.Maximum - origin);
if (renderContext.View.IsCullingDisabled || frustum.Intersects(bounds)) if (renderContext.View.IsCullingDisabled || frustum.Intersects(bounds))
{ {
if (chunk->PrepareDraw(renderContext)) if (!drawnChunks.Contains(chunk) && !chunk->PrepareDraw(renderContext))
{ continue;
// Add chunk for drawing
_drawChunks.Add(chunk); // 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 // 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);
} }
} }

View File

@@ -72,7 +72,6 @@ private:
Vector3 _boundsExtent; Vector3 _boundsExtent;
Float3 _cachedScale; Float3 _cachedScale;
Array<TerrainPatch*, InlinedAllocation<64>> _patches; Array<TerrainPatch*, InlinedAllocation<64>> _patches;
Array<TerrainChunk*> _drawChunks;
Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>> _physicalMaterials; Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>> _physicalMaterials;
public: public:
@@ -424,9 +423,12 @@ private:
#if TERRAIN_USE_PHYSICS_DEBUG #if TERRAIN_USE_PHYSICS_DEBUG
void DrawPhysicsDebug(RenderView& view); void DrawPhysicsDebug(RenderView& view);
#endif #endif
bool DrawSetup(RenderContext& renderContext);
void DrawImpl(RenderContext& renderContext, HashSet<TerrainChunk*, class RendererAllocation>& drawnChunks);
public: public:
// [PhysicsColliderActor] // [PhysicsColliderActor]
void Draw(RenderContextBatch& renderContextBatch) override;
void Draw(RenderContext& renderContext) override; void Draw(RenderContext& renderContext) override;
#if USE_EDITOR #if USE_EDITOR
void OnDebugDrawSelected() override; void OnDebugDrawSelected() override;

View File

@@ -34,9 +34,9 @@ private:
float _perInstanceRandom; float _perInstanceRandom;
float _yOffset, _yHeight; float _yOffset, _yHeight;
TerrainChunk* _neighbors[4]; TerrainChunk* _neighbors[4] = {};
byte _cachedDrawLOD; byte _cachedDrawLOD = 0;
IMaterial* _cachedDrawMaterial; IMaterial* _cachedDrawMaterial = nullptr;
void Init(TerrainPatch* patch, uint16 x, uint16 z); void Init(TerrainPatch* patch, uint16 x, uint16 z);

View File

@@ -113,6 +113,11 @@ TerrainPatch::~TerrainPatch()
#endif #endif
} }
RawDataAsset* TerrainPatch::GetHeightfield() const
{
return _heightfield.Get();
}
void TerrainPatch::RemoveLightmap() void TerrainPatch::RemoveLightmap()
{ {
for (auto& chunk : Chunks) for (auto& chunk : Chunks)
@@ -1215,7 +1220,8 @@ Color32* TerrainPatch::GetSplatMapData(int32 index)
void TerrainPatch::ClearSplatMapCache() void TerrainPatch::ClearSplatMapCache()
{ {
PROFILE_CPU_NAMED("Terrain.ClearSplatMapCache"); PROFILE_CPU_NAMED("Terrain.ClearSplatMapCache");
_cachedSplatMap->Clear(); if (_cachedSplatMap)
_cachedSplatMap->Clear();
} }
void TerrainPatch::ClearCache() void TerrainPatch::ClearCache()

View File

@@ -149,6 +149,12 @@ public:
return GetChunk(z * Terrain::ChunksCountEdge + x); return GetChunk(z * Terrain::ChunksCountEdge + x);
} }
/// <summary>
/// Gets the heightfield collision data asset.
/// </summary>
/// <returns>The heightfield data asset.</returns>
API_PROPERTY() RawDataAsset* GetHeightfield() const;
/// <summary> /// <summary>
/// Gets the splatmap assigned to this patch. /// Gets the splatmap assigned to this patch.
/// </summary> /// </summary>