From 0a516ac98d86464eef859a3752a57601a7cf2a65 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 14 Sep 2024 15:33:07 +0200 Subject: [PATCH 01/17] make it possible to pan editor when rmb down on node --- Source/Editor/Surface/SurfaceNode.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index c5c011db0..09ab04c8a 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -146,6 +146,8 @@ namespace FlaxEditor.Surface /// protected virtual Color FooterColor => GroupArchetype.Color; + private Float2 mouseDownMousePosition; + /// /// Calculates the size of the node including header, footer, and margins. /// @@ -1093,7 +1095,7 @@ namespace FlaxEditor.Surface if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location)) return true; if (button == MouseButton.Right) - return true; + mouseDownMousePosition = Input.Mouse.Position; return false; } @@ -1114,6 +1116,9 @@ namespace FlaxEditor.Surface // Secondary Context Menu if (button == MouseButton.Right) { + if (mouseDownMousePosition != Input.Mouse.Position) + return true; + if (!IsSelected) Surface.Select(this); var tmp = PointToParent(ref location); From e665cc75003022173fd1cbdc78361e7397f5d31f Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 16 Sep 2024 15:43:46 +0200 Subject: [PATCH 02/17] add safe margin 4 pixels seemed a bit much, smaller adjustments could lead to the menu still showing --- Source/Editor/Surface/SurfaceNode.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 09ab04c8a..f763eceb5 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -1116,7 +1116,8 @@ namespace FlaxEditor.Surface // Secondary Context Menu if (button == MouseButton.Right) { - if (mouseDownMousePosition != Input.Mouse.Position) + float distance = Float2.Distance(mouseDownMousePosition, Input.Mouse.Position); + if (distance > 2.5f) return true; if (!IsSelected) From 797cb3c3f2afefe543f0e22de64e60041e12ead4 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 23 Sep 2025 20:56:43 +0200 Subject: [PATCH 03/17] fix 1.10 becoming 1.1 and some spelling issue in issue template --- .github/ISSUE_TEMPLATE/1-bug.yaml | 12 ++++++------ .github/ISSUE_TEMPLATE/2-feature-request.yaml | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/1-bug.yaml b/.github/ISSUE_TEMPLATE/1-bug.yaml index acae26c00..4a58ed11d 100644 --- a/.github/ISSUE_TEMPLATE/1-bug.yaml +++ b/.github/ISSUE_TEMPLATE/1-bug.yaml @@ -5,7 +5,7 @@ body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! Please attach any minimal repoduction projects! + Thanks for taking the time to fill out this bug report! Please attach any minimal reproduction projects! - type: textarea id: description-area attributes: @@ -17,7 +17,7 @@ body: id: steps-area attributes: label: Steps to reproduce - description: Please provide an repoduction steps. + description: Please provide an reproduction steps. validations: required: true - type: dropdown @@ -26,10 +26,10 @@ body: label: Version description: What version of Flax are you running? options: - - 1.8 - - 1.9 - - 1.10 - - 1.11 + - '1.8' + - '1.9' + - '1.10' + - '1.11' - master branch default: 2 validations: diff --git a/.github/ISSUE_TEMPLATE/2-feature-request.yaml b/.github/ISSUE_TEMPLATE/2-feature-request.yaml index 235c863e3..338c9aea0 100644 --- a/.github/ISSUE_TEMPLATE/2-feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/2-feature-request.yaml @@ -14,9 +14,9 @@ body: validations: required: true - type: textarea - id: benifits-area + id: benefits-area attributes: - label: Benifits - description: Please provide what benifits this feature would provide to the engine! + label: Benefits + description: Please provide what benefits this feature would provide to the engine! validations: required: true \ No newline at end of file From 4a3fb410350194a8bfd0ac574acbcacfeb9d8e8b Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 25 Sep 2025 13:32:21 +0200 Subject: [PATCH 04/17] further fixes and make some stuff a bit nicer --- .github/ISSUE_TEMPLATE/1-bug.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/1-bug.yaml b/.github/ISSUE_TEMPLATE/1-bug.yaml index 4a58ed11d..2e2c65485 100644 --- a/.github/ISSUE_TEMPLATE/1-bug.yaml +++ b/.github/ISSUE_TEMPLATE/1-bug.yaml @@ -10,14 +10,14 @@ body: id: description-area attributes: label: Description - description: Please provide a description and what you expected to happen. + description: Please provide a description of the bug and what you expected to happen. validations: required: true - type: textarea id: steps-area attributes: label: Steps to reproduce - description: Please provide an reproduction steps. + description: Please provide reproduction steps if possible. validations: required: true - type: dropdown From 9cc2c1da4050fc22d967711611b05e1b933360bc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Sep 2025 22:58:08 +0200 Subject: [PATCH 05/17] Fix terrain exporting to properly calculate size and sample positions #3534 --- Source/Editor/Tools/Terrain/TerrainTools.cpp | 110 +++++++------------ 1 file changed, 38 insertions(+), 72 deletions(-) diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 2a17f7960..065f7ffeb 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -149,13 +149,13 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool h bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches, Texture* heightmap, float heightmapScale, Texture* splatmap1, Texture* splatmap2) { + PROFILE_CPU_NAMED("Terrain.GenerateTerrain"); CHECK_RETURN(terrain && terrain->GetChunkSize() != 0, true); if (numberOfPatches.X < 1 || numberOfPatches.Y < 1) { - LOG(Warning, "Cannot setup terain with no patches."); + LOG(Warning, "Cannot setup terrain with no patches."); return false; } - PROFILE_CPU_NAMED("Terrain.GenerateTerrain"); // Wait for assets to be loaded if (heightmap && heightmap->WaitForLoaded()) @@ -178,7 +178,9 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches terrain->AddPatches(numberOfPatches); // Prepare data - const auto heightmapSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1; + const int32 heightmapSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1; + const float heightmapSizeInv = 1.0f / (float)(heightmapSize - 1); + const Float2 uvPerPatch = Float2::One / Float2(numberOfPatches); Array heightmapData; heightmapData.Resize(heightmapSize * heightmapSize); @@ -192,19 +194,17 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches const auto sampler = PixelFormatSampler::Get(dataHeightmap.Format); // Initialize with sub-range of the input heightmap - const Vector2 uvPerPatch = Vector2::One / Vector2(numberOfPatches); - const float heightmapSizeInv = 1.0f / (heightmapSize - 1); for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++) { auto patch = terrain->GetPatch(patchIndex); - const Vector2 uvStart = Vector2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch; + const Float2 uvStart = Float2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch; // Sample heightmap pixels with interpolation to get actual heightmap vertices locations for (int32 z = 0; z < heightmapSize; z++) { for (int32 x = 0; x < heightmapSize; x++) { - const Vector2 uv = uvStart + Vector2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch; + const Float2 uv = uvStart + Float2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch; const Color color = sampler->SampleLinear(dataHeightmap.Mip0DataPtr->Get(), uv, dataHeightmap.Mip0Size, dataHeightmap.RowPitch); heightmapData[z * heightmapSize + x] = color.R * heightmapScale; } @@ -230,37 +230,30 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches Texture* splatmaps[2] = { splatmap1, splatmap2 }; Array splatmapData; TextureDataResult data1; - const Vector2 uvPerPatch = Vector2::One / Vector2(numberOfPatches); - const float heightmapSizeInv = 1.0f / (heightmapSize - 1); for (int32 index = 0; index < ARRAY_COUNT(splatmaps); index++) { const auto splatmap = splatmaps[index]; if (!splatmap) continue; - // Prepare data - if (splatmapData.IsEmpty()) - splatmapData.Resize(heightmapSize * heightmapSize); - // Get splatmap data if (GetTextureDataForSampling(splatmap, data1)) return true; const auto sampler = PixelFormatSampler::Get(data1.Format); // Modify heightmap splatmaps with sub-range of the input splatmaps + splatmapData.Resize(heightmapSize * heightmapSize); for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++) { auto patch = terrain->GetPatch(patchIndex); - - const Vector2 uvStart = Vector2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch; + const Float2 uvStart = Float2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch; // Sample splatmap pixels with interpolation to get actual splatmap values for (int32 z = 0; z < heightmapSize; z++) { for (int32 x = 0; x < heightmapSize; x++) { - const Vector2 uv = uvStart + Vector2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch; - + const Float2 uv = uvStart + Float2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch; const Color color = sampler->SampleLinear(data1.Mip0DataPtr->Get(), uv, data1.Mip0Size, data1.RowPitch); Color32 layers; @@ -374,63 +367,38 @@ Color32* TerrainTools::GetSplatMapData(Terrain* terrain, const Int2& patchCoord, bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder) { + PROFILE_CPU_NAMED("Terrain.ExportTerrain"); CHECK_RETURN(terrain && terrain->GetPatchesCount() != 0, true); - const auto firstPatch = terrain->GetPatch(0); - - // Calculate texture size - const int32 patchEdgeVertexCount = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1; - const int32 patchVertexCount = patchEdgeVertexCount * patchEdgeVertexCount; // Find size of heightmap in patches + const auto firstPatch = terrain->GetPatch(0); Int2 start(firstPatch->GetX(), firstPatch->GetZ()); Int2 end(start); - for (int32 i = 0; i < terrain->GetPatchesCount(); i++) + for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++) { - const int32 x = terrain->GetPatch(i)->GetX(); - const int32 y = terrain->GetPatch(i)->GetZ(); - - if (x < start.X) - start.X = x; - if (y < start.Y) - start.Y = y; - if (x > end.X) - end.X = x; - if (y > end.Y) - end.Y = y; + const auto patch = terrain->GetPatch(patchIndex); + const Int2 pos(patch->GetX(), patch->GetZ()); + start = Int2::Min(start, pos); + end = Int2::Max(end, pos); } const Int2 size = (end + 1) - start; - // Allocate - with space for non-existent patches + // Allocate heightmap for a whole terrain (NumberOfPatches * 4x4 * ChunkSize + 1) + const Int2 heightmapSize = size * Terrain::ChunksCountEdge * terrain->GetChunkSize() + 1; Array heightmap; - heightmap.Resize(patchVertexCount * size.X * size.Y); - - // Set to any element, where: min < elem < max + heightmap.Resize(heightmapSize.X * heightmapSize.Y); heightmap.SetAll(firstPatch->GetHeightmapData()[0]); - const int32 heightmapWidth = patchEdgeVertexCount * size.X; - - // Fill heightmap with data + // Fill heightmap with data from all patches + const int32 rowSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1; for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++) { - // Pick a patch const auto patch = terrain->GetPatch(patchIndex); - const float* data = patch->GetHeightmapData(); - - // Beginning of patch - int32 dstIndex = (patch->GetX() - start.X) * patchEdgeVertexCount + - (patch->GetZ() - start.Y) * size.Y * patchVertexCount; - - // Iterate over lines in patch - for (int32 z = 0; z < patchEdgeVertexCount; z++) - { - // Iterate over vertices in line - for (int32 x = 0; x < patchEdgeVertexCount; x++) - { - heightmap[dstIndex + x] = data[z * patchEdgeVertexCount + x]; - } - - dstIndex += heightmapWidth; - } + const Int2 pos(patch->GetX() - start.X, patch->GetZ() - start.Y); + const float* src = patch->GetHeightmapData(); + float* dst = heightmap.Get() + pos.X * (rowSize - 1) + pos.Y * heightmapSize.X * (rowSize - 1); + for (int32 row = 0; row < rowSize; row++) + Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float)); } // Interpolate to 16-bit int @@ -438,44 +406,42 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder) maxHeight = minHeight = heightmap[0]; for (int32 i = 1; i < heightmap.Count(); i++) { - float h = heightmap[i]; + float h = heightmap.Get()[i]; if (maxHeight < h) maxHeight = h; else if (minHeight > h) minHeight = h; } - - const float maxValue = 65535.0f; - const float alpha = maxValue / (maxHeight - minHeight); + const float alpha = MAX_uint16 / (maxHeight - minHeight); // Storage for pixel data - Array byteHeightmap(heightmap.Capacity()); - - for (auto& elem : heightmap) + Array byteHeightmap; + byteHeightmap.Resize(heightmap.Count()); + for (int32 i = 0; i < heightmap.Count(); i++) { - byteHeightmap.Add(static_cast(alpha * (elem - minHeight))); + float height = heightmap.Get()[i]; + byteHeightmap.Get()[i] = static_cast(alpha * (height - minHeight)); } // Create texture TextureData textureData; - textureData.Height = textureData.Width = heightmapWidth; + textureData.Width = heightmapSize.X; + textureData.Height = heightmapSize.Y; textureData.Depth = 1; textureData.Format = PixelFormat::R16_UNorm; textureData.Items.Resize(1); textureData.Items[0].Mips.Resize(1); - - // Fill mip data TextureMipData* srcMip = textureData.GetData(0, 0); srcMip->Data.Link(byteHeightmap.Get()); srcMip->Lines = textureData.Height; - srcMip->RowPitch = textureData.Width * 2; // 2 bytes per pixel for format R16 + srcMip->RowPitch = textureData.Width * sizeof(uint16); srcMip->DepthPitch = srcMip->Lines * srcMip->RowPitch; // Find next non-existing file heightmap file FileSystem::NormalizePath(outputFolder); const String baseFileName(TEXT("heightmap")); String outputPath; - for (int32 i = 0; i < MAX_int32; i++) + for (int32 i = 0; i < 100; i++) { outputPath = outputFolder / baseFileName + StringUtils::ToString(i) + TEXT(".png"); if (!FileSystem::FileExists(outputPath)) From 028b5fedecdde43d9beb44c921143f83ee6777ce Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Sep 2025 23:37:15 +0200 Subject: [PATCH 06/17] Use right-click only on GPUTextureEditor context menu --- Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs b/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs index 954059a28..2daa3c0a5 100644 --- a/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs @@ -32,7 +32,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private void OnImageClicked(Image image, MouseButton button) { var texture = Values[0] as GPUTexture; - if (!texture) + if (!texture || button != MouseButton.Right) return; var menu = new ContextMenu(); menu.AddButton("Save...", () => Screenshot.Capture(Values[0] as GPUTexture)); From 7e1ac5e167cea42b0a77cfc220845ae90a7263b3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 2 Oct 2025 18:48:14 +0200 Subject: [PATCH 07/17] Fix sky rendering in ortho and oblique projection #3448 --- Content/Shaders/Sky.flax | 4 +- Source/Engine/Level/Actors/Sky.cpp | 74 +++----------------------- Source/Engine/Level/Actors/Sky.h | 4 +- Source/Engine/Renderer/GBufferPass.cpp | 14 ++++- Source/Shaders/Sky.shader | 57 ++++---------------- 5 files changed, 34 insertions(+), 119 deletions(-) diff --git a/Content/Shaders/Sky.flax b/Content/Shaders/Sky.flax index 87a5b6b3a..a568db451 100644 --- a/Content/Shaders/Sky.flax +++ b/Content/Shaders/Sky.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28d44ef295d989d49ad4d59d6581eee921bc5fa5237e046d271a433454496388 -size 3463 +oid sha256:8541d1f0590167498f44da231f6de4f2938e15b26a5eb58e27b5067e1c882be8 +size 2252 diff --git a/Source/Engine/Level/Actors/Sky.cpp b/Source/Engine/Level/Actors/Sky.cpp index 4e635489f..6d7d5efe2 100644 --- a/Source/Engine/Level/Actors/Sky.cpp +++ b/Source/Engine/Level/Actors/Sky.cpp @@ -21,7 +21,8 @@ #endif GPU_CB_STRUCT(Data { - Matrix WVP; + Matrix WorldViewProjection; + Matrix InvViewProjection; Float3 ViewOffset; float Padding; ShaderGBufferData GBuffer; @@ -30,9 +31,6 @@ GPU_CB_STRUCT(Data { Sky::Sky(const SpawnParams& params) : Actor(params) - , _shader(nullptr) - , _psSky(nullptr) - , _psFog(nullptr) { _drawNoCulling = 1; _drawCategory = SceneRendering::PreRender; @@ -52,7 +50,6 @@ Sky::Sky(const SpawnParams& params) Sky::~Sky() { SAFE_DELETE_GPU_RESOURCE(_psSky); - SAFE_DELETE_GPU_RESOURCE(_psFog); } void Sky::InitConfig(ShaderAtmosphericFogData& config) const @@ -91,7 +88,7 @@ void Sky::Draw(RenderContext& renderContext) if (HasContentLoaded() && EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Sky)) { // Ensure to have pipeline state cache created - if (_psSky == nullptr || _psFog == nullptr) + if (_psSky == nullptr) { const auto shader = _shader->GetShader(); @@ -113,21 +110,6 @@ void Sky::Draw(RenderContext& renderContext) LOG(Warning, "Cannot create graphics pipeline state object for '{0}'.", ToString()); } } - if (_psFog == nullptr) - { - _psFog = GPUDevice::Instance->CreatePipelineState(); - - GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle; - psDesc.PS = shader->GetPS("PS_Fog"); - psDesc.DepthWriteEnable = false; - psDesc.DepthClipEnable = false; - psDesc.BlendMode = BlendingMode::Additive; - - if (_psFog->Init(psDesc)) - { - LOG(Warning, "Cannot create graphics pipeline state object for '{0}'.", ToString()); - } - } } // Register for the sky and fog pass @@ -139,7 +121,6 @@ void Sky::Draw(RenderContext& renderContext) void Sky::Serialize(SerializeStream& stream, const void* otherObj) { - // Base Actor::Serialize(stream, otherObj); SERIALIZE_GET_OTHER_OBJ(Sky); @@ -152,7 +133,6 @@ void Sky::Serialize(SerializeStream& stream, const void* otherObj) void Sky::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) { - // Base Actor::Deserialize(stream, modifier); DESERIALIZE_MEMBER(Sun, SunLight); @@ -173,39 +153,7 @@ bool Sky::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) void Sky::DrawFog(GPUContext* context, RenderContext& renderContext, GPUTextureView* output) { - // Get precomputed cache and bind it to the pipeline - AtmosphereCache cache; - if (!AtmospherePreCompute::GetCache(&cache)) - return; - context->BindSR(4, cache.Transmittance); - context->BindSR(5, cache.Irradiance); - context->BindSR(6, cache.Inscatter->ViewVolume()); - - // Bind GBuffer inputs - context->BindSR(0, renderContext.Buffers->GBuffer0); - context->BindSR(1, renderContext.Buffers->GBuffer1); - context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, renderContext.Buffers->DepthBuffer); - - // Setup constants data - Data data; - GBufferPass::SetInputs(renderContext.View, data.GBuffer); - data.ViewOffset = renderContext.View.Origin + GetPosition(); - InitConfig(data.Fog); - data.Fog.AtmosphericFogSunPower *= SunLight ? SunLight->Brightness : 1.0f; - bool useSpecularLight = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::SpecularLight); - if (!useSpecularLight) - { - data.Fog.AtmosphericFogSunDiscScale = 0; - } - - // Bind pipeline - auto cb = _shader->GetShader()->GetCB(0); - context->UpdateCB(cb, &data); - context->BindCB(0, cb); - context->SetState(_psFog); - context->SetRenderTarget(output); - context->DrawFullscreenTriangle(); + MISSING_CODE("sky fog"); } bool Sky::IsDynamicSky() const @@ -231,14 +179,14 @@ void Sky::ApplySky(GPUContext* context, RenderContext& renderContext, const Matr // Setup constants data Matrix m; Data data; - Matrix::Multiply(world, renderContext.View.Frustum.GetMatrix(), m); - Matrix::Transpose(m, data.WVP); + Matrix::Multiply(world, renderContext.View.ViewProjection(), m); + Matrix::Transpose(m, data.WorldViewProjection); + Matrix::Transpose(renderContext.View.IVP, data.InvViewProjection); GBufferPass::SetInputs(renderContext.View, data.GBuffer); data.ViewOffset = renderContext.View.Origin + GetPosition(); InitConfig(data.Fog); //data.Fog.AtmosphericFogSunPower *= SunLight ? SunLight->Brightness : 1.0f; - bool useSpecularLight = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::SpecularLight); - if (!useSpecularLight) + if (EnumHasNoneFlags(renderContext.View.Flags, ViewFlags::SpecularLight)) { // Hide sun disc if specular light is disabled data.Fog.AtmosphericFogSunDiscScale = 0; @@ -253,11 +201,8 @@ void Sky::ApplySky(GPUContext* context, RenderContext& renderContext, const Matr void Sky::EndPlay() { - // Cleanup SAFE_DELETE_GPU_RESOURCE(_psSky); - SAFE_DELETE_GPU_RESOURCE(_psFog); - // Base Actor::EndPlay(); } @@ -268,7 +213,6 @@ void Sky::OnEnable() GetSceneRendering()->AddViewportIcon(this); #endif - // Base Actor::OnEnable(); } @@ -279,13 +223,11 @@ void Sky::OnDisable() #endif GetSceneRendering()->RemoveActor(this, _sceneRenderingKey); - // Base Actor::OnDisable(); } void Sky::OnTransformChanged() { - // Base Actor::OnTransformChanged(); _box = BoundingBox(_transform.Translation); diff --git a/Source/Engine/Level/Actors/Sky.h b/Source/Engine/Level/Actors/Sky.h index 65bcc631a..61bb048ef 100644 --- a/Source/Engine/Level/Actors/Sky.h +++ b/Source/Engine/Level/Actors/Sky.h @@ -20,8 +20,7 @@ class FLAXENGINE_API Sky : public Actor, public IAtmosphericFogRenderer, public DECLARE_SCENE_OBJECT(Sky); private: AssetReference _shader; - GPUPipelineState* _psSky; - GPUPipelineState* _psFog; + GPUPipelineState* _psSky = nullptr; int32 _sceneRenderingKey = -1; public: @@ -57,7 +56,6 @@ private: void OnShaderReloading(Asset* obj) { _psSky = nullptr; - _psFog = nullptr; } #endif void InitConfig(ShaderAtmosphericFogData& config) const; diff --git a/Source/Engine/Renderer/GBufferPass.cpp b/Source/Engine/Renderer/GBufferPass.cpp index 9d3684010..7c9abb413 100644 --- a/Source/Engine/Renderer/GBufferPass.cpp +++ b/Source/Engine/Renderer/GBufferPass.cpp @@ -19,6 +19,7 @@ #include "Engine/Content/Content.h" #include "Engine/Content/Assets/Model.h" #include "Engine/Level/Actors/Decal.h" +#include "Engine/Level/Actors/Sky.h" #include "Engine/Engine/Engine.h" GPU_CB_STRUCT(GBufferPassData { @@ -416,8 +417,17 @@ void GBufferPass::DrawSky(RenderContext& renderContext, GPUContext* context) // Calculate sphere model transform to cover far plane Matrix m1, m2; - Matrix::Scaling(renderContext.View.Far / ((float)box.GetSize().Y * 0.5f) * 0.95f, m1); // Scale to fit whole view frustum - Matrix::CreateWorld(renderContext.View.Position, Float3::Up, Float3::Backward, m2); // Rotate sphere model + float size = renderContext.View.Far; + Float3 origin = renderContext.View.Position; + if (dynamic_cast(renderContext.List->Sky)) // TODO: refactor sky rendering (eg. let sky draw with custom projection) + { + BoundingSphere frustumBounds; + renderContext.View.CullingFrustum.GetSphere(frustumBounds); + origin = frustumBounds.Center; + size = frustumBounds.Radius; + } + Matrix::Scaling(size / ((float)box.GetSize().Y * 0.5f) * 0.95f, m1); // Scale to fit whole view frustum + Matrix::CreateWorld(origin, Float3::Up, Float3::Backward, m2); // Rotate sphere model m1 *= m2; // Draw sky diff --git a/Source/Shaders/Sky.shader b/Source/Shaders/Sky.shader index af5d0053c..de29e5d3f 100644 --- a/Source/Shaders/Sky.shader +++ b/Source/Shaders/Sky.shader @@ -7,7 +7,8 @@ #include "./Flax/AtmosphereFog.hlsl" META_CB_BEGIN(0, Data) -float4x4 WVP; +float4x4 WorldViewProjection; +float4x4 InvViewProjection; float3 ViewOffset; float Padding; GBufferData GBuffer; @@ -18,7 +19,7 @@ DECLARE_GBUFFERDATA_ACCESS(GBuffer) struct MaterialInput { - float4 Position : SV_Position; + float4 Position : SV_Position; float4 ScreenPos : TEXCOORD0; }; @@ -30,12 +31,9 @@ MaterialInput VS(ModelInput_PosOnly input) MaterialInput output; // Compute vertex position - output.Position = mul(float4(input.Position.xyz, 1), WVP); + output.Position = mul(float4(input.Position.xyz, 1), WorldViewProjection); output.ScreenPos = output.Position; - // Place pixels on the far plane - output.Position = output.Position.xyzz; - return output; } @@ -45,15 +43,15 @@ GBufferOutput PS_Sky(MaterialInput input) { GBufferOutput output; - // Obtain UVs corresponding to the current pixel - float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5); + // Calculate view vector (unproject at the far plane) + GBufferData gBufferData = GetGBufferData(); + float4 clipPos = float4(input.ScreenPos.xy / input.ScreenPos.w, 1.0, 1.0); + clipPos = mul(clipPos, InvViewProjection); + float3 worldPos = clipPos.xyz / clipPos.w; + float3 viewVector = normalize(worldPos - gBufferData.ViewPos); // Sample atmosphere color - GBufferData gBufferData = GetGBufferData(); - float3 vsPos = GetViewPos(gBufferData, uv, LinearZ2DeviceDepth(gBufferData, 1)); - float3 wsPos = mul(float4(vsPos, 1), gBufferData.InvViewMatrix).xyz; - float3 viewVector = wsPos - gBufferData.ViewPos; - float4 color = GetAtmosphericFog(AtmosphericFog, gBufferData.ViewFar, wsPos + ViewOffset, gBufferData.ViewPos + ViewOffset); + float4 color = GetAtmosphericFog(AtmosphericFog, gBufferData.ViewFar, gBufferData.ViewPos + ViewOffset, viewVector, gBufferData.ViewFar, float3(0, 0, 0)); // Pack GBuffer output.Light = color; @@ -64,36 +62,3 @@ GBufferOutput PS_Sky(MaterialInput input) return output; } - -META_PS(true, FEATURE_LEVEL_ES2) -float4 PS_Fog(Quad_VS2PS input) : SV_Target0 -{ - float4 result; - /* - // Sample GBuffer - GBufferSample gBuffer = SampleGBuffer(GBuffer, input.TexCoord); - - // TODO: set valid scene color for better inscatter reflectance - //float3 sceneColor = gBuffer.Color * AtmosphericFogDensityOffset; - float3 sceneColor = float3(0, 0, 0); - - // Sample atmosphere color - float3 viewVector = gBuffer.WorldPos - GBuffer.ViewPos; - float SceneDepth = length(ViewVector); - result = GetAtmosphericFog(AtmosphericFog, GBuffer.ViewFar, GBuffer.ViewPos, viewVector, SceneDepth, sceneColor); - - //result.rgb = normalize(ViewVector); - //result.rgb = ViewVector; - //result.rgb = SceneDepth.xxx / GBuffer.ViewFar * 0.5f; - - //result = float4(input.TexCoord, 0, 1); - //result = AtmosphereTransmittanceTexture.Sample(SamplerLinearClamp, input.TexCoord); - //result = float4(AtmosphereIrradianceTexture.Sample(SamplerLinearClamp, input.TexCoord).rgb*5.0, 1.0); - //result = AtmosphereInscatterTexture.Sample(SamplerLinearClamp, float3(input.TexCoord.xy, (AtmosphericFogSunDirection.x+1.0)/2.0)); - */ - - // TODO: finish fog - result = float4(1, 0, 0, 1); - - return result; -} From 9b812ec34a0022b2b3f604424fae75ceecb3af69 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 2 Oct 2025 18:48:32 +0200 Subject: [PATCH 08/17] Fix `BoundingFrustum::GetPlane` in C++ to match C# version (and doc comment) --- Source/Engine/Core/Math/BoundingFrustum.cpp | 13 ++++++++++--- Source/Engine/Core/Math/BoundingFrustum.cs | 2 +- Source/Engine/Core/Math/BoundingFrustum.h | 4 ++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Core/Math/BoundingFrustum.cpp b/Source/Engine/Core/Math/BoundingFrustum.cpp index e296fa1a8..e0697ce5e 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.cpp +++ b/Source/Engine/Core/Math/BoundingFrustum.cpp @@ -63,9 +63,16 @@ void BoundingFrustum::SetMatrix(const Matrix& matrix) Plane BoundingFrustum::GetPlane(int32 index) const { - if (index > 5) - return Plane(); - return _planes[index]; + switch (index) + { + case 0: return _pLeft; + case 1: return _pRight; + case 2: return _pTop; + case 3: return _pBottom; + case 4: return _pNear; + case 5: return _pFar; + default: return Plane(); + } } static Vector3 Get3PlanesInterPoint(const Plane& p1, const Plane& p2, const Plane& p3) diff --git a/Source/Engine/Core/Math/BoundingFrustum.cs b/Source/Engine/Core/Math/BoundingFrustum.cs index 6f6e034ce..4f1e27e1e 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.cs +++ b/Source/Engine/Core/Math/BoundingFrustum.cs @@ -182,7 +182,7 @@ namespace FlaxEngine /// /// Returns one of the 6 planes related to this frustum. /// - /// Plane index where 0 fro Left, 1 for Right, 2 for Top, 3 for Bottom, 4 for Near, 5 for Far + /// Plane index where 0 for Left, 1 for Right, 2 for Top, 3 for Bottom, 4 for Near, 5 for Far /// The frustum plane. public Plane GetPlane(int index) { diff --git a/Source/Engine/Core/Math/BoundingFrustum.h b/Source/Engine/Core/Math/BoundingFrustum.h index 30eb70f43..a3a5442e9 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.h +++ b/Source/Engine/Core/Math/BoundingFrustum.h @@ -148,13 +148,13 @@ public: Plane GetPlane(int32 index) const; /// - /// Gets the the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner). + /// Gets the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner). /// /// The corners. void GetCorners(Float3 corners[8]) const; /// - /// Gets the the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner). + /// Gets the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner). /// /// The corners. void GetCorners(Double3 corners[8]) const; From adcfc5021801b8a3fc730dff1a294f199c8d5b10 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 2 Oct 2025 20:49:50 +0200 Subject: [PATCH 09/17] Fix stack overflow exception in shader graph recursion to be detected #3706 --- Source/Engine/Visject/ShaderGraph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Visject/ShaderGraph.h b/Source/Engine/Visject/ShaderGraph.h index 84c4b64d0..ab0e7d405 100644 --- a/Source/Engine/Visject/ShaderGraph.h +++ b/Source/Engine/Visject/ShaderGraph.h @@ -14,7 +14,7 @@ #include "Engine/Content/AssetsContainer.h" #include "Engine/Animations/Curve.h" -#define SHADER_GRAPH_MAX_CALL_STACK 100 +#define SHADER_GRAPH_MAX_CALL_STACK 50 enum class MaterialSceneTextures; template From 60c19303f65b4fc08c2c46d6881c18dda94c77bc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 2 Oct 2025 23:31:03 +0200 Subject: [PATCH 10/17] Fix duplicating splines with parents #3531 --- Source/Editor/Modules/SceneEditingModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 3c7130615..c96e79ed9 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -673,6 +673,7 @@ namespace FlaxEditor.Modules pasteAction.Do(out _, out var nodeParents); // Select spawned objects (parents only) + newSelection.Clear(); newSelection.AddRange(nodeParents); var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo); selectAction.Do(); From 1f3f1ea67eb71567154ea1ac94c9c2f5a556579d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 3 Oct 2025 10:52:01 +0200 Subject: [PATCH 11/17] Fix Blend Poses nodes to smoothly blend back when transition goes back #3595 --- .../Animations/Graph/AnimGraph.Base.cpp | 1 + Source/Engine/Animations/Graph/AnimGraph.h | 3 +- .../Animations/Graph/AnimGroup.Animation.cpp | 40 ++++++++----------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp index c49f0e26e..e4d7eda70 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp @@ -93,6 +93,7 @@ void MultiBlendBucketInit(AnimGraphInstanceData::Bucket& bucket) void BlendPoseBucketInit(AnimGraphInstanceData::Bucket& bucket) { bucket.BlendPose.TransitionPosition = 0.0f; + bucket.BlendPose.BlendPoseIndex = -1; bucket.BlendPose.PreviousBlendPoseIndex = -1; } diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index e03d84fd9..6c36e37a5 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -239,7 +239,8 @@ public: struct BlendPoseBucket { float TransitionPosition; - int32 PreviousBlendPoseIndex; + int16 BlendPoseIndex; + int16 PreviousBlendPoseIndex; }; struct StateMachineBucket diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index f75a8abd1..c76bddf3f 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -676,9 +676,12 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const if (!ANIM_GRAPH_IS_VALID_PTR(poseB)) nodesB = GetEmptyNodes(); + const Transform* srcA = nodesA->Nodes.Get(); + const Transform* srcB = nodesB->Nodes.Get(); + Transform* dst = nodes->Nodes.Get(); for (int32 i = 0; i < nodes->Nodes.Count(); i++) { - Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]); + Transform::Lerp(srcA[i], srcB[i], alpha, dst[i]); } Transform::Lerp(nodesA->RootMotion, nodesB->RootMotion, alpha, nodes->RootMotion); nodes->Position = Math::Lerp(nodesA->Position, nodesB->Position, alpha); @@ -1263,21 +1266,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu { const auto valueA = tryGetValue(node->GetBox(1), Value::Null); const auto valueB = tryGetValue(node->GetBox(2), Value::Null); - const auto nodes = node->GetNodes(this); - - auto nodesA = static_cast(valueA.AsPointer); - auto nodesB = static_cast(valueB.AsPointer); - if (!ANIM_GRAPH_IS_VALID_PTR(valueA)) - nodesA = GetEmptyNodes(); - if (!ANIM_GRAPH_IS_VALID_PTR(valueB)) - nodesB = GetEmptyNodes(); - - for (int32 i = 0; i < nodes->Nodes.Count(); i++) - { - Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]); - } - Transform::Lerp(nodesA->RootMotion, nodesB->RootMotion, alpha, nodes->RootMotion); - value = nodes; + value = Blend(node, valueA, valueB, alpha, AlphaBlendMode::Linear); } break; @@ -1758,35 +1747,38 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // [2]: int Pose Count // [3]: AlphaBlendMode Mode - // Prepare auto& bucket = context.Data->State[node->BucketIndex].BlendPose; - const int32 poseIndex = (int32)tryGetValue(node->GetBox(1), node->Values[0]); + const int16 poseIndex = (int32)tryGetValue(node->GetBox(1), node->Values[0]); const float blendDuration = (float)tryGetValue(node->GetBox(2), node->Values[1]); const int32 poseCount = Math::Clamp(node->Values[2].AsInt, 0, MaxBlendPoses); const AlphaBlendMode mode = (AlphaBlendMode)node->Values[3].AsInt; - - // Skip if nothing to blend if (poseCount == 0 || poseIndex < 0 || poseIndex >= poseCount) - { break; + + // Check if swap transition end points + if (bucket.PreviousBlendPoseIndex == poseIndex && bucket.BlendPoseIndex != poseIndex && bucket.TransitionPosition >= ANIM_GRAPH_BLEND_THRESHOLD) + { + bucket.TransitionPosition = blendDuration - bucket.TransitionPosition; + Swap(bucket.BlendPoseIndex, bucket.PreviousBlendPoseIndex); } // Check if transition is not active (first update, pose not changing or transition ended) bucket.TransitionPosition += context.DeltaTime; + bucket.BlendPoseIndex = poseIndex; if (bucket.PreviousBlendPoseIndex == -1 || bucket.PreviousBlendPoseIndex == poseIndex || bucket.TransitionPosition >= blendDuration || blendDuration <= ANIM_GRAPH_BLEND_THRESHOLD) { bucket.TransitionPosition = 0.0f; + bucket.BlendPoseIndex = poseIndex; bucket.PreviousBlendPoseIndex = poseIndex; - value = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null); + value = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.BlendPoseIndex), Value::Null); break; } - ASSERT(bucket.PreviousBlendPoseIndex >= 0 && bucket.PreviousBlendPoseIndex < poseCount); // Blend two animations { const float alpha = bucket.TransitionPosition / blendDuration; const auto valueA = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.PreviousBlendPoseIndex), Value::Null); - const auto valueB = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null); + const auto valueB = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.BlendPoseIndex), Value::Null); value = Blend(node, valueA, valueB, alpha, mode); } From 4c640b915fe573b4e40960308fbd6201e1599e7b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 3 Oct 2025 11:25:25 +0200 Subject: [PATCH 12/17] Fix `CheckBox` to display check state when created in game without editor icon atlas #3705 --- Source/Engine/UI/GUI/Common/CheckBox.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs index c779d6e83..47073cb27 100644 --- a/Source/Engine/UI/GUI/Common/CheckBox.cs +++ b/Source/Engine/UI/GUI/Common/CheckBox.cs @@ -181,8 +181,8 @@ namespace FlaxEngine.GUI ImageColor = style.BorderSelected * 1.2f; BorderColor = style.BorderNormal; BorderColorHighlighted = style.BorderSelected; - CheckedImage = new SpriteBrush(style.CheckBoxTick); - IntermediateImage = new SpriteBrush(style.CheckBoxIntermediate); + CheckedImage = style.CheckBoxTick.IsValid ? new SpriteBrush(style.CheckBoxTick) : new SolidColorBrush(style.Foreground); + IntermediateImage = style.CheckBoxIntermediate.IsValid ? new SpriteBrush(style.CheckBoxIntermediate) : new SolidColorBrush(style.ForegroundGrey); CacheBox(); } From 4f45b3c1d0377da75226fe55030fbb8751fc3d70 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 3 Oct 2025 11:33:01 +0200 Subject: [PATCH 13/17] Fix missing root motion copy when using input skeleton pose (eg. IK node) #3445 --- Source/Engine/Animations/Graph/AnimGraph.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index 6c36e37a5..051f6613d 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -811,6 +811,7 @@ public: { // Copy the node transformations Platform::MemoryCopy(dstNodes->Nodes.Get(), srcNodes->Nodes.Get(), sizeof(Transform) * _skeletonNodesCount); + dstNodes->RootMotion = srcNodes->RootMotion; // Copy the animation playback state dstNodes->Position = srcNodes->Position; From da5c8555e594eb8d7c6c715ca235437baa2e57e7 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 3 Oct 2025 21:16:28 +0200 Subject: [PATCH 14/17] add myself to list of Flax authors lol Mafi said I should do this :) --- Source/Editor/Windows/AboutDialog.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index b970c9e09..1d9afbdfe 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -97,6 +97,7 @@ namespace FlaxEditor.Windows "Jean-Baptiste Perrier", "Chandler Cox", "Ari Vuollet", + "Vincent Saarmann", }); authors.Sort(); var authorsLabel = new Label(4, topParentControl.Bottom + 20, Width - 8, 70) From 6ae370f8fc6e7377016d3e79813b4b3634244933 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 3 Oct 2025 22:20:58 +0200 Subject: [PATCH 15/17] Fix camera cut flag in rendering to not trigger on origin change for smother visuals --- Source/Engine/Graphics/RenderView.h | 8 ++++++++ Source/Engine/Renderer/Renderer.cpp | 2 -- Source/Engine/Renderer/VolumetricFogPass.cpp | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h index e7b60491c..a85480e3e 100644 --- a/Source/Engine/Graphics/RenderView.h +++ b/Source/Engine/Graphics/RenderView.h @@ -265,6 +265,14 @@ public: return Projection.M44 >= 1.0f; } + /// + /// Determines whether view Origin has been moved in this frame. Old history buffers/data might be invalid. + /// + FORCE_INLINE bool IsOriginTeleport() const + { + return Origin != PrevOrigin; + } + public: // Ignore deprecation warnings in defaults PRAGMA_DISABLE_DEPRECATION_WARNINGS diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 7fd12ba73..cd2c9ca2b 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -353,8 +353,6 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont const bool isGBufferDebug = GBufferPass::IsDebugView(renderContext.View.Mode); { PROFILE_CPU_NAMED("Setup"); - if (renderContext.View.Origin != renderContext.View.PrevOrigin) - renderContext.Task->CameraCut(); // Cut any temporal effects on rendering origin change const int32 screenWidth = renderContext.Buffers->GetWidth(); const int32 screenHeight = renderContext.Buffers->GetHeight(); setup.UpscaleLocation = renderContext.Task->UpscaleLocation; diff --git a/Source/Engine/Renderer/VolumetricFogPass.cpp b/Source/Engine/Renderer/VolumetricFogPass.cpp index 2fa957ab1..4cb672fc2 100644 --- a/Source/Engine/Renderer/VolumetricFogPass.cpp +++ b/Source/Engine/Renderer/VolumetricFogPass.cpp @@ -179,7 +179,7 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context, (float)_cache.GridSizeZ); auto& fogData = renderContext.Buffers->VolumetricFogData; fogData.MaxDistance = options.Distance; - if (renderContext.Task->IsCameraCut) + if (renderContext.Task->IsCameraCut || renderContext.View.IsOriginTeleport()) _cache.HistoryWeight = 0.0f; // Init data (partial, without directional light or sky light data); @@ -313,7 +313,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext) PROFILE_GPU_CPU("Volumetric Fog"); // TODO: test exponential depth distribution (should give better quality near the camera) - // TODO: use tiled light culling and render unshadowed lights in single pass + // TODO: use tiled light culling and render shadowed/unshadowed lights in single pass // Try to get shadows atlas GPUTexture* shadowMap; From 4f1f77fb321db286f035b5062258c19a8e50b5af Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 3 Oct 2025 22:21:11 +0200 Subject: [PATCH 16/17] Simplify code of EditorViewport to access editor instance --- Source/Editor/Viewport/EditorViewport.cs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 0f9fce7c0..c16d3d9f5 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -535,7 +535,7 @@ namespace FlaxEditor.Viewport // Setup options { - Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; + _editor.Options.OptionsChanged += OnEditorOptionsChanged; SetupViewportOptions(); } @@ -581,7 +581,7 @@ namespace FlaxEditor.Viewport // Camera Settings Menu var cameraCM = new ContextMenu(); - _cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), Editor.Instance.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth) + _cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), _editor.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth) { Tag = this, TooltipText = "Camera Settings", @@ -590,7 +590,7 @@ namespace FlaxEditor.Viewport _cameraWidget.Parent = this; // Orthographic/Perspective Mode Widget - _orthographicModeButton = new ViewportWidgetButton(string.Empty, Editor.Instance.Icons.CamSpeed32, null, true) + _orthographicModeButton = new ViewportWidgetButton(string.Empty, _editor.Icons.CamSpeed32, null, true) { Checked = !_isOrtho, TooltipText = "Toggle Orthographic/Perspective Mode", @@ -863,8 +863,8 @@ namespace FlaxEditor.Viewport { } }); - viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = Editor.Instance.Icons.Rotate32; - viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)).Icon = Editor.Instance.Icons.Rotate32; + viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = _editor.Icons.Rotate32; + viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)); viewLayers.AddSeparator(); var layers = LayersAndTagsSettings.GetCurrentLayers(); if (layers != null && layers.Length > 0) @@ -904,8 +904,8 @@ namespace FlaxEditor.Viewport { } }); - viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32; - viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32; + viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = _editor.Icons.Rotate32; + viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None); viewFlags.AddSeparator(); for (int i = 0; i < ViewFlagsValues.Length; i++) { @@ -1085,7 +1085,7 @@ namespace FlaxEditor.Viewport /// private void SetupViewportOptions() { - var options = Editor.Instance.Options.Options; + var options = _editor.Options.Options; _minMovementSpeed = options.Viewport.MinMovementSpeed; MovementSpeed = options.Viewport.MovementSpeed; _maxMovementSpeed = options.Viewport.MaxMovementSpeed; @@ -1701,7 +1701,7 @@ namespace FlaxEditor.Viewport // Check if update mouse var size = Size; - var options = Editor.Instance.Options.Options; + var options = _editor.Options.Options; if (_isControllingMouse) { var rmbWheel = false; @@ -1926,7 +1926,7 @@ namespace FlaxEditor.Viewport return true; // Custom input events - return InputActions.Process(Editor.Instance, this, key); + return InputActions.Process(_editor, this, key); } /// @@ -1943,7 +1943,7 @@ namespace FlaxEditor.Viewport base.Draw(); // Add overlay during debugger breakpoint hang - if (Editor.Instance.Simulation.IsDuringBreakpointHang) + if (_editor.Simulation.IsDuringBreakpointHang) { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); @@ -1967,7 +1967,7 @@ namespace FlaxEditor.Viewport /// public override void OnDestroy() { - Editor.Instance.Options.OptionsChanged -= OnEditorOptionsChanged; + _editor.Options.OptionsChanged -= OnEditorOptionsChanged; base.OnDestroy(); } From 8f3b80492ed2d9c44f3366ab0af784b8f1a206a1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 3 Oct 2025 22:30:44 +0200 Subject: [PATCH 17/17] Fix terrain physics error on end play when it's disabled #3590 #3603 --- Source/Engine/Terrain/Terrain.cpp | 4 ---- Source/Engine/Terrain/TerrainPatch.cpp | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index f13d3bcc4..ace6246b2 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -852,9 +852,7 @@ void Terrain::OnEnable() { auto patch = _patches[i]; if (patch->_physicsActor) - { PhysicsBackend::AddSceneActor(scene, patch->_physicsActor); - } } // Base @@ -873,9 +871,7 @@ void Terrain::OnDisable() { auto patch = _patches[i]; if (patch->_physicsActor) - { PhysicsBackend::RemoveSceneActor(scene, patch->_physicsActor); - } } // Base diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index a9388224d..f73a7552c 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -2218,7 +2218,8 @@ void TerrainPatch::DestroyCollision() void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene(); PhysicsBackend::RemoveCollider(_terrain); - PhysicsBackend::RemoveSceneActor(scene, _physicsActor); + if (_terrain->IsDuringPlay() && _terrain->IsActiveInHierarchy()) + PhysicsBackend::RemoveSceneActor(scene, _physicsActor); PhysicsBackend::DestroyActor(_physicsActor); PhysicsBackend::DestroyShape(_physicsShape); PhysicsBackend::DestroyObject(_physicsHeightField);