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

# Conflicts:
#	Content/Shaders/GI/DDGI.flax
#	Content/Shaders/GUI.flax
#	Flax.flaxproj
#	Source/Editor/Windows/AboutDialog.cs
#	Source/Engine/Serialization/Stream.cpp
#	Source/Shaders/GUICommon.hlsl
This commit is contained in:
Wojtek Figat
2026-03-13 08:09:16 +01:00
152 changed files with 3784 additions and 434 deletions

View File

@@ -5,6 +5,7 @@
#include "Level.h"
#include "SceneQuery.h"
#include "SceneObjectsFactory.h"
#include "FlaxEngine.Gen.h"
#include "Scene/Scene.h"
#include "Prefabs/Prefab.h"
#include "Prefabs/PrefabManager.h"
@@ -178,21 +179,20 @@ void Actor::OnDeleteObject()
_scene = nullptr;
}
}
else if (_parent)
else
{
// Unlink from the parent
_parent->Children.RemoveKeepOrder(this);
_parent->_isHierarchyDirty = true;
_parent = nullptr;
_scene = nullptr;
if (_isEnabled)
OnDisable();
if (_parent)
{
// Unlink from the parent
_parent->Children.RemoveKeepOrder(this);
_parent->_isHierarchyDirty = true;
_parent = nullptr;
_scene = nullptr;
}
}
// Ensure to exit gameplay in a valid way
ASSERT(!IsDuringPlay());
#if BUILD_DEBUG || BUILD_DEVELOPMENT
ASSERT(!_isEnabled);
#endif
// Fire event
Deleted(this);

View File

@@ -1,6 +1,7 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "PointLight.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/RenderView.h"
@@ -196,6 +197,14 @@ void PointLight::Deserialize(DeserializeStream& stream, ISerializeModifier* modi
DESERIALIZE(UseInverseSquaredFalloff);
DESERIALIZE(UseIESBrightness);
DESERIALIZE(IESBrightnessScale);
// [Deprecated on 12.03.2026, expires on 12.03.2028]
if (modifier->EngineBuild <= 6807 && SERIALIZE_FIND_MEMBER(stream, "UseInverseSquaredFalloff") != stream.MemberEnd() && UseInverseSquaredFalloff)
{
// Convert old non-physical brightness value that was used for Inverse Squared Falloff which wasn't based on proper cm/m units calculations
MARK_CONTENT_DEPRECATED();
Brightness = Math::Sqrt(Brightness * 0.01f);
}
}
bool PointLight::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal)

View File

@@ -1,6 +1,8 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "SpotLight.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Content/Assets/IESProfile.h"
@@ -282,6 +284,14 @@ void SpotLight::Deserialize(DeserializeStream& stream, ISerializeModifier* modif
DESERIALIZE(UseInverseSquaredFalloff);
DESERIALIZE(UseIESBrightness);
DESERIALIZE(IESBrightnessScale);
// [Deprecated on 12.03.2026, expires on 12.03.2028]
if (modifier->EngineBuild <= 6807 && SERIALIZE_FIND_MEMBER(stream, "UseInverseSquaredFalloff") != stream.MemberEnd() && UseInverseSquaredFalloff)
{
// Convert old non-physical brightness value that was used for Inverse Squared Falloff which wasn't based on proper cm/m units calculations
MARK_CONTENT_DEPRECATED();
Brightness = Math::Sqrt(Brightness * 0.01f);
}
}
bool SpotLight::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal)

View File

@@ -66,6 +66,20 @@ void StaticModel::SetBoundsScale(float value)
UpdateBounds();
}
DrawPass StaticModel::GetDrawModes() const
{
return _drawModes;
}
void StaticModel::SetDrawModes(DrawPass value)
{
if (_drawModes == value)
return;
_drawModes = value;
if (_sceneRenderingKey != -1)
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::DrawModes);
}
int32 StaticModel::GetLODBias() const
{
return _lodBias;
@@ -330,13 +344,13 @@ void StaticModel::Draw(RenderContext& renderContext)
return;
if (renderContext.View.Pass == DrawPass::GlobalSDF)
{
if (EnumHasAnyFlags(DrawModes, DrawPass::GlobalSDF) && Model->SDF.Texture)
if (EnumHasAnyFlags(_drawModes, DrawPass::GlobalSDF) && Model->SDF.Texture)
GlobalSignDistanceFieldPass::Instance()->RasterizeModelSDF(this, Model->SDF, _transform, _box);
return;
}
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
{
if (EnumHasAnyFlags(DrawModes, DrawPass::GlobalSurfaceAtlas) && Model->SDF.Texture)
if (EnumHasAnyFlags(_drawModes, DrawPass::GlobalSurfaceAtlas) && Model->SDF.Texture)
GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, this, _sphere, _transform, Model->LODs.Last().GetBox());
return;
}
@@ -353,7 +367,7 @@ void StaticModel::Draw(RenderContext& renderContext)
draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr;
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = DrawModes;
draw.DrawModes = _drawModes;
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
@@ -390,7 +404,7 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr;
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = DrawModes;
draw.DrawModes = _drawModes;
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
@@ -435,7 +449,7 @@ void StaticModel::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(LODBias, _lodBias);
SERIALIZE_MEMBER(ForcedLOD, _forcedLod);
SERIALIZE_MEMBER(SortOrder, _sortOrder);
SERIALIZE(DrawModes);
SERIALIZE_MEMBER(DrawModes, _drawModes);
if (HasLightmap()
#if USE_EDITOR
@@ -487,7 +501,7 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
DESERIALIZE_MEMBER(LODBias, _lodBias);
DESERIALIZE_MEMBER(ForcedLOD, _forcedLod);
DESERIALIZE_MEMBER(SortOrder, _sortOrder);
DESERIALIZE(DrawModes);
DESERIALIZE_MEMBER(DrawModes, _drawModes);
DESERIALIZE_MEMBER(LightmapIndex, Lightmap.TextureIndex);
DESERIALIZE_MEMBER(LightmapArea, Lightmap.UVsArea);
@@ -537,27 +551,27 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
if (member != stream.MemberEnd() && member->value.IsBool() && member->value.GetBool())
{
MARK_CONTENT_DEPRECATED();
DrawModes = DrawPass::Depth;
_drawModes = DrawPass::Depth;
}
}
// [Deprecated on 07.02.2022, expires on 07.02.2024]
if (modifier->EngineBuild <= 6330)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSDF;
_drawModes |= DrawPass::GlobalSDF;
}
// [Deprecated on 27.04.2022, expires on 27.04.2024]
if (modifier->EngineBuild <= 6331)
{
MARK_CONTENT_DEPRECATED();
DrawModes |= DrawPass::GlobalSurfaceAtlas;
_drawModes |= DrawPass::GlobalSurfaceAtlas;
}
{
const auto member = stream.FindMember("RenderPasses");
if (member != stream.MemberEnd() && member->value.IsInt())
{
DrawModes = (DrawPass)member->value.GetInt();
_drawModes = (DrawPass)member->value.GetInt();
}
}
}

View File

@@ -23,6 +23,7 @@ private:
bool _vertexColorsDirty;
byte _vertexColorsCount;
int8 _sortOrder;
DrawPass _drawModes = DrawPass::Default;
Array<Color32> _vertexColorsData[MODEL_MAX_LODS];
GPUBuffer* _vertexColorsBuffer[MODEL_MAX_LODS];
Model* _residencyChangedModel = nullptr;
@@ -40,12 +41,6 @@ public:
API_FIELD(Attributes="EditorOrder(20), DefaultValue(null), EditorDisplay(\"Model\")")
AssetReference<Model> Model;
/// <summary>
/// The draw passes to use for rendering this object.
/// </summary>
API_FIELD(Attributes="EditorOrder(15), DefaultValue(DrawPass.Default), EditorDisplay(\"Model\")")
DrawPass DrawModes = DrawPass::Default;
/// <summary>
/// The baked lightmap entry.
/// </summary>
@@ -74,6 +69,17 @@ public:
/// </summary>
API_PROPERTY() void SetBoundsScale(float value);
/// <summary>
/// Gets the draw passes to use for rendering this object.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(15), DefaultValue(DrawPass.Default), EditorDisplay(\"Model\")")
DrawPass GetDrawModes() const;
/// <summary>
/// Sets the draw passes to use for rendering this object.
/// </summary>
API_PROPERTY() void SetDrawModes(DrawPass value);
/// <summary>
/// Gets the model Level Of Detail bias value. Allows to increase or decrease rendered model quality.
/// </summary>

View File

@@ -5,6 +5,7 @@
#include "LargeWorlds.h"
#include "SceneQuery.h"
#include "SceneObjectsFactory.h"
#include "FlaxEngine.Gen.h"
#include "Scene/Scene.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
@@ -960,9 +961,6 @@ bool LevelImpl::unloadScene(Scene* scene)
// Simple enqueue scene root object to be deleted
scene->DeleteObject();
// Force flush deleted objects so we actually delete unloaded scene objects (prevent from reloading their managed objects, etc.)
ObjectsRemovalService::Flush();
return false;
}
@@ -1125,6 +1123,32 @@ SceneResult SceneLoader::OnBegin(Args& args)
_lastSceneLoadTime = DateTime::Now();
StartFrame = Engine::UpdateCount;
// Validate arguments
if (!args.Data.IsArray())
{
LOG(Error, "Invalid Data member.");
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, Guid::Empty);
return SceneResult::Failed;
}
// Peek scene node value (it's the first actor serialized)
SceneId = JsonTools::GetGuid(args.Data[0], "ID");
if (!SceneId.IsValid())
{
LOG(Error, "Invalid scene id.");
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, SceneId);
return SceneResult::Failed;
}
// Peek meta
if (args.EngineBuild < 6000)
{
LOG(Error, "Invalid serialized engine build.");
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, SceneId);
return SceneResult::Failed;
}
Modifier->EngineBuild = args.EngineBuild;
// Scripting backend should be loaded for the current project before loading scene
if (!Scripting::HasGameModulesLoaded())
{
@@ -1138,27 +1162,7 @@ SceneResult SceneLoader::OnBegin(Args& args)
MessageBox::Show(TEXT("Failed to load scripts.\n\nCannot load scene without game script modules.\n\nSee logs for more info."), TEXT("Missing game modules"), MessageBoxButtons::OK, MessageBoxIcon::Error);
}
#endif
return SceneResult::Failed;
}
// Peek meta
if (args.EngineBuild < 6000)
{
LOG(Error, "Invalid serialized engine build.");
return SceneResult::Failed;
}
if (!args.Data.IsArray())
{
LOG(Error, "Invalid Data member.");
return SceneResult::Failed;
}
Modifier->EngineBuild = args.EngineBuild;
// Peek scene node value (it's the first actor serialized)
SceneId = JsonTools::GetGuid(args.Data[0], "ID");
if (!SceneId.IsValid())
{
LOG(Error, "Invalid scene id.");
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, SceneId);
return SceneResult::Failed;
}
@@ -1166,6 +1170,7 @@ SceneResult SceneLoader::OnBegin(Args& args)
if (Level::FindScene(SceneId) != nullptr)
{
LOG(Info, "Scene {0} is already loaded.", SceneId);
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, SceneId);
return SceneResult::Failed;
}

View File

@@ -389,11 +389,14 @@ void Scene::BeginPlay(SceneBeginData* data)
if (model == nullptr)
CreateCsgModel();
}
Ticking.SetTicking(true);
}
void Scene::EndPlay()
{
// Improve scene cleanup performance by removing all data from scene rendering and ticking containers
Ticking.SetTicking(false);
Ticking.Clear();
Rendering.Clear();
Navigation.Clear();

View File

@@ -56,6 +56,7 @@ public:
Layer = 4,
StaticFlags = 8,
AutoDelayDuringRendering = 16, // Conditionally allow updating data during rendering when writes are locked
DrawModes = 32,
Auto = Visual | Bounds | Layer,
};

View File

@@ -44,7 +44,7 @@ void SceneTicking::TickData::Tick()
{
TickScripts(Scripts);
for (int32 i = 0; i < Ticks.Count(); i++)
for (int32 i = 0; i < Ticks.Count() && _canTick; i++)
Ticks.Get()[i].Call();
}
@@ -66,7 +66,7 @@ void SceneTicking::TickData::TickExecuteInEditor()
{
TickScripts(ScriptsExecuteInEditor);
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
for (int32 i = 0; i < TicksExecuteInEditor.Count() && _canTick; i++)
TicksExecuteInEditor.Get()[i].Call();
}
@@ -89,10 +89,8 @@ SceneTicking::FixedUpdateTickData::FixedUpdateTickData()
void SceneTicking::FixedUpdateTickData::TickScripts(const Array<Script*>& scripts)
{
for (auto* script : scripts)
{
script->OnFixedUpdate();
}
for (int32 i = 0; i < scripts.Count() && _canTick; i++)
scripts.Get()[i]->OnFixedUpdate();
}
SceneTicking::UpdateTickData::UpdateTickData()
@@ -102,36 +100,30 @@ SceneTicking::UpdateTickData::UpdateTickData()
void SceneTicking::UpdateTickData::TickScripts(const Array<Script*>& scripts)
{
for (auto* script : scripts)
{
script->OnUpdate();
}
for (int32 i = 0; i < scripts.Count() && _canTick; i++)
scripts.Get()[i]->OnUpdate();
}
SceneTicking::LateUpdateTickData::LateUpdateTickData()
: TickData(64)
: TickData(0)
{
}
void SceneTicking::LateUpdateTickData::TickScripts(const Array<Script*>& scripts)
{
for (auto* script : scripts)
{
script->OnLateUpdate();
}
for (int32 i = 0; i < scripts.Count() && _canTick; i++)
scripts.Get()[i]->OnLateUpdate();
}
SceneTicking::LateFixedUpdateTickData::LateFixedUpdateTickData()
: TickData(64)
: TickData(0)
{
}
void SceneTicking::LateFixedUpdateTickData::TickScripts(const Array<Script*>& scripts)
{
for (auto* script : scripts)
{
script->OnLateFixedUpdate();
}
for (int32 i = 0; i < scripts.Count() && _canTick; i++)
scripts.Get()[i]->OnLateFixedUpdate();
}
void SceneTicking::AddScript(Script* obj)
@@ -167,3 +159,11 @@ void SceneTicking::Clear()
LateUpdate.Clear();
LateFixedUpdate.Clear();
}
void SceneTicking::SetTicking(bool enable)
{
FixedUpdate._canTick = enable;
Update._canTick = enable;
LateUpdate._canTick = enable;
LateFixedUpdate._canTick = enable;
}

View File

@@ -46,6 +46,9 @@ public:
/// </summary>
class FLAXENGINE_API TickData
{
protected:
friend SceneTicking;
bool _canTick = true;
public:
Array<Script*> Scripts;
Array<Tick> Ticks;
@@ -134,6 +137,11 @@ public:
/// </summary>
void Clear();
/// <summary>
/// Changes the ability to tick. When disabled, the ticking functions won't be called. Can be called during ticking (eg. when scene is unloaded) to stp performing any more ticks.
/// </summary>
void SetTicking(bool enable);
public:
/// <summary>
/// The fixed update tick function.