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

This commit is contained in:
Wojtek Figat
2023-11-20 11:16:07 +01:00
41 changed files with 779 additions and 540 deletions

View File

@@ -728,9 +728,7 @@ namespace Math
/// <summary>
/// Returns value based on comparand. The main purpose of this function is to avoid branching based on floating point comparison which can be avoided via compiler intrinsics.
/// </summary>
/// <remarks>
/// Please note that this doesn't define what happens in the case of NaNs as there might be platform specific differences.
/// </remarks>
/// <remarks>Please note that this doesn't define what happens in the case of NaNs as there might be platform specific differences.</remarks>
/// <param name="comparand">Comparand the results are based on.</param>
/// <param name="valueGEZero">The result value if comparand >= 0.</param>
/// <param name="valueLTZero">The result value if comparand < 0.</param>
@@ -891,6 +889,18 @@ namespace Math
return Lerp<T>(a, b, alpha < 0.5f ? InterpCircularIn(0.f, 1.f, alpha * 2.f) * 0.5f : InterpCircularOut(0.f, 1.f, alpha * 2.f - 1.f) * 0.5f + 0.5f);
}
/// <summary>
/// Ping pongs the value <paramref name="t"/>, so that it is never larger than <paramref name="length"/> and never smaller than 0.
/// </summary>
/// <param name="t"></param>
/// <param name="length"></param>
/// <returns></returns>
template<class T>
static FORCE_INLINE T PingPong(const T& t, T length)
{
return length - Abs(Repeat(t, length * 2.0f) - length);
}
// Rotates position about the given axis by the given angle, in radians, and returns the offset to position
Vector3 FLAXENGINE_API RotateAboutAxis(const Vector3& normalizedRotationAxis, float angle, const Vector3& positionOnAxis, const Vector3& position);

View File

@@ -3,6 +3,7 @@
#pragma once
#include "String.h"
#include "StringView.h"
#include "Engine/Core/Collections/Array.h"
/// <summary>
@@ -138,6 +139,11 @@ public:
_data.Add(*str, str.Length());
return *this;
}
StringBuilder& Append(const StringView& str)
{
_data.Add(*str, str.Length());
return *this;
}
// Append int to the string
// @param val Value to append

View File

@@ -591,37 +591,37 @@ API_ENUM() enum class Quality : byte
API_ENUM() enum class MaterialPostFxLocation : byte
{
/// <summary>
/// The 'after' post processing pass using LDR input frame.
/// Render the material after the post processing pass using *LDR* input frame.
/// </summary>
AfterPostProcessingPass = 0,
/// <summary>
/// The 'before' post processing pass using HDR input frame.
/// Render the material before the post processing pass using *HDR* input frame.
/// </summary>
BeforePostProcessingPass = 1,
/// <summary>
/// The 'before' forward pass but after GBuffer with HDR input frame.
/// Render the material before the forward pass but after *GBuffer* with *HDR* input frame.
/// </summary>
BeforeForwardPass = 2,
/// <summary>
/// The 'after' custom post effects.
/// Render the material after custom post effects (scripted).
/// </summary>
AfterCustomPostEffects = 3,
/// <summary>
/// The 'before' Reflections pass. After the Light pass. Can be used to implement a custom light types that accumulate lighting to the light buffer.
/// Render the material before the reflections pass but after the lighting pass using *HDR* input frame. It can be used to implement a custom light types that accumulate lighting to the light buffer.
/// </summary>
BeforeReflectionsPass = 4,
/// <summary>
/// The 'after' AA filter pass. Rendering is done to the output backbuffer.
/// Render the material after anti-aliasing into the output backbuffer.
/// </summary>
AfterAntiAliasingPass = 5,
/// <summary>
/// The 'after' forward pass but before any post processing.
/// Render the material after the forward pass but before any post processing.
/// </summary>
AfterForwardPass = 6,

View File

@@ -67,7 +67,8 @@ void GraphicsSettings::Apply()
Graphics::AllowCSMBlending = AllowCSMBlending;
Graphics::GlobalSDFQuality = GlobalSDFQuality;
Graphics::GIQuality = GIQuality;
Graphics::PostProcessSettings = PostProcessSettings;
Graphics::PostProcessSettings = ::PostProcessSettings();
Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f);
}
void Graphics::DisposeDevice()

View File

@@ -447,13 +447,13 @@ API_STRUCT() struct FLAXENGINE_API BloomSettings : ISerializable
bool Enabled = true;
/// <summary>
/// Bloom effect strength. Value 0 disabled is, while higher values increase the effect.
/// Bloom effect strength. Set a value of 0 to disabled it, while higher values increase the effect.
/// </summary>
API_FIELD(Attributes="Limit(0, 20.0f, 0.01f), EditorOrder(1), PostProcessSetting((int)BloomSettingsOverride.Intensity)")
float Intensity = 1.0f;
/// <summary>
/// Minimum pixel brightness value to start blowing. Values below the threshold are skipped.
/// Minimum pixel brightness value to start blooming. Values below this threshold are skipped.
/// </summary>
API_FIELD(Attributes="Limit(0, 15.0f, 0.01f), EditorOrder(2), PostProcessSetting((int)BloomSettingsOverride.Threshold)")
float Threshold = 3.0f;
@@ -987,13 +987,13 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable
float MaxBrightness = 2.0f;
/// <summary>
/// The lower bound for the luminance histogram of the scene color. Value is in percent and limits the pixels below this brightness. Use values from range 60-80. Used only in AutomaticHistogram mode.
/// The lower bound for the luminance histogram of the scene color. This value is in percent and limits the pixels below this brightness. Use values in the range of 60-80. Used only in AutomaticHistogram mode.
/// </summary>
API_FIELD(Attributes="Limit(1, 99, 0.001f), EditorOrder(3), PostProcessSetting((int)EyeAdaptationSettingsOverride.HistogramLowPercent)")
float HistogramLowPercent = 70.0f;
/// <summary>
/// The upper bound for the luminance histogram of the scene color. Value is in percent and limits the pixels above this brightness. Use values from range 80-95. Used only in AutomaticHistogram mode.
/// The upper bound for the luminance histogram of the scene color. This value is in percent and limits the pixels above this brightness. Use values in the range of 80-95. Used only in AutomaticHistogram mode.
/// </summary>
API_FIELD(Attributes="Limit(1, 99, 0.001f), EditorOrder(3), PostProcessSetting((int)EyeAdaptationSettingsOverride.HistogramHighPercent)")
float HistogramHighPercent = 98.0f;
@@ -1091,13 +1091,13 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
Float3 VignetteColor = Float3(0, 0, 0.001f);
/// <summary>
/// Controls shape of the vignette. Values near 0 produce rectangle shape. Higher values result in round shape. The default value is 0.125.
/// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125.
/// </summary>
API_FIELD(Attributes="Limit(0.0001f, 2.0f, 0.001f), EditorOrder(2), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteShapeFactor)")
float VignetteShapeFactor = 0.125f;
/// <summary>
/// Intensity of the grain filter. Value 0 hides it. The default value is 0.005.
/// Intensity of the grain filter. A value of 0 hides it. The default value is 0.005.
/// </summary>
API_FIELD(Attributes="Limit(0.0f, 2.0f, 0.005f), EditorOrder(3), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainAmount)")
float GrainAmount = 0.006f;
@@ -1109,19 +1109,19 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
float GrainParticleSize = 1.6f;
/// <summary>
/// Speed of the grain particles animation.
/// Speed of the grain particle animation.
/// </summary>
API_FIELD(Attributes="Limit(0.0f, 10.0f, 0.01f), EditorOrder(5), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainSpeed)")
float GrainSpeed = 1.0f;
/// <summary>
/// Controls chromatic aberration effect strength. Value 0 hides it.
/// Controls the chromatic aberration effect strength. A value of 0 hides it.
/// </summary>
API_FIELD(Attributes="Limit(0.0f, 1.0f, 0.01f), EditorOrder(6), PostProcessSetting((int)CameraArtifactsSettingsOverride.ChromaticDistortion)")
float ChromaticDistortion = 0.0f;
/// <summary>
/// Screen tint color (alpha channel defines the blending factor).
/// Screen tint color (the alpha channel defines the blending factor).
/// </summary>
API_FIELD(Attributes="DefaultValue(typeof(Color), \"0,0,0,0\"), EditorOrder(7), PostProcessSetting((int)CameraArtifactsSettingsOverride.ScreenFadeColor)")
Color ScreenFadeColor = Color::Transparent;
@@ -1227,7 +1227,7 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
LensFlaresSettingsOverride OverrideFlags = Override::None;
/// <summary>
/// Strength of the effect. Value 0 disabled it.
/// Strength of the effect. A value of 0 disables it.
/// </summary>
API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(0), PostProcessSetting((int)LensFlaresSettingsOverride.Intensity)")
float Intensity = 1.0f;
@@ -1281,7 +1281,7 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
SoftAssetReference<Texture> LensDirt;
/// <summary>
/// Fullscreen lens dirt intensity parameter. Allows to tune dirt visibility.
/// Fullscreen lens dirt intensity parameter. Allows tuning dirt visibility.
/// </summary>
API_FIELD(Attributes="Limit(0, 100, 0.01f), EditorOrder(9), PostProcessSetting((int)LensFlaresSettingsOverride.LensDirtIntensity)")
float LensDirtIntensity = 1.0f;
@@ -1419,13 +1419,13 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
DepthOfFieldSettingsOverride OverrideFlags = Override::None;
/// <summary>
/// If checked, depth of field effect will be visible.
/// If checked, the depth of field effect will be visible.
/// </summary>
API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)DepthOfFieldSettingsOverride.Enabled)")
bool Enabled = false;
/// <summary>
/// The blur intensity in the out-of-focus areas. Allows reducing blur amount by scaling down the Gaussian Blur radius. Normalized to range 0-1.
/// The blur intensity in the out-of-focus areas. Allows reducing the blur amount by scaling down the Gaussian Blur radius. Normalized to range 0-1.
/// </summary>
API_FIELD(Attributes="Limit(0, 1, 0.01f), EditorOrder(1), PostProcessSetting((int)DepthOfFieldSettingsOverride.BlurStrength)")
float BlurStrength = 1.0f;
@@ -1479,7 +1479,7 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
float BokehBrightness = 1.0f;
/// <summary>
/// Defines bokeh shapes type.
/// Defines the type of the bokeh shapes.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehShape)")
BokehShapeType BokehShape = BokehShapeType::Octagon;
@@ -1491,19 +1491,19 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
SoftAssetReference<Texture> BokehShapeCustom;
/// <summary>
/// The minimum pixel brightness to create bokeh. Pixels with lower brightness will be skipped.
/// The minimum pixel brightness to create the bokeh. Pixels with lower brightness will be skipped.
/// </summary>
API_FIELD(Attributes="Limit(0, 10000.0f, 0.01f), EditorOrder(12), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehBrightnessThreshold)")
float BokehBrightnessThreshold = 3.0f;
/// <summary>
/// Depth of Field bokeh shapes blur threshold.
/// Depth of Field bokeh shape blur threshold.
/// </summary>
API_FIELD(Attributes="Limit(0, 1.0f, 0.001f), EditorOrder(13), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehBlurThreshold)")
float BokehBlurThreshold = 0.05f;
/// <summary>
/// Controls bokeh shapes brightness falloff. Higher values reduce bokeh visibility.
/// Controls bokeh shape brightness falloff. Higher values reduce bokeh visibility.
/// </summary>
API_FIELD(Attributes="Limit(0, 2.0f, 0.001f), EditorOrder(14), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehFalloff)")
float BokehFalloff = 0.5f;
@@ -1575,25 +1575,25 @@ API_STRUCT() struct FLAXENGINE_API MotionBlurSettings : ISerializable
MotionBlurSettingsOverride OverrideFlags = Override::None;
/// <summary>
/// If checked, motion blur effect will be rendered.
/// If checked, the motion blur effect will be rendered.
/// </summary>
API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)MotionBlurSettingsOverride.Enabled)")
bool Enabled = true;
/// <summary>
/// The blur effect strength. Value 0 disabled is, while higher values increase the effect.
/// The blur effect strength. A value of 0 disables it, while higher values increase the effect.
/// </summary>
API_FIELD(Attributes="Limit(0, 5, 0.01f), EditorOrder(1), PostProcessSetting((int)MotionBlurSettingsOverride.Scale)")
float Scale = 1.0f;
/// <summary>
/// The amount of sample points used during motion blur rendering. It affects quality and performance.
/// The amount of sample points used during motion blur rendering. It affects blur quality and performance.
/// </summary>
API_FIELD(Attributes="Limit(4, 32, 0.1f), EditorOrder(2), PostProcessSetting((int)MotionBlurSettingsOverride.SampleCount)")
int32 SampleCount = 10;
/// <summary>
/// The motion vectors texture resolution. Motion blur uses per-pixel motion vectors buffer that contains objects movement information. Use lower resolution to improve performance.
/// The motion vectors texture resolution. Motion blur uses a per-pixel motion vector buffer that contains an objects movement information. Use a lower resolution to improve performance.
/// </summary>
API_FIELD(Attributes="EditorOrder(3), PostProcessSetting((int)MotionBlurSettingsOverride.MotionVectorsResolution)")
ResolutionMode MotionVectorsResolution = ResolutionMode::Half;
@@ -1898,13 +1898,13 @@ API_STRUCT() struct FLAXENGINE_API AntiAliasingSettings : ISerializable
float TAA_Sharpness = 0.0f;
/// <summary>
/// The blend coefficient for stationary fragments. Controls the percentage of history sample blended into final color for fragments with minimal active motion.
/// The blend coefficient for stationary fragments. Controls the percentage of history samples blended into the final color for fragments with minimal active motion.
/// </summary>
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(3), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_StationaryBlending), EditorDisplay(null, \"TAA Stationary Blending\")")
float TAA_StationaryBlending = 0.95f;
/// <summary>
/// The blending coefficient for moving fragments. Controls the percentage of history sample blended into the final color for fragments with significant active motion.
/// The blending coefficient for moving fragments. Controls the percentage of history samples blended into the final color for fragments with significant active motion.
/// </summary>
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(4), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_MotionBlending), EditorDisplay(null, \"TAA Motion Blending\")")
float TAA_MotionBlending = 0.7f;

View File

@@ -29,7 +29,7 @@ public:
float FogDensity = 0.02f;
/// <summary>
/// The fog height density factor that controls how the density increases as height decreases. The smaller values produce more visible transition larger.
/// The fog height density factor that controls how the density increases as height decreases. Smaller values produce a more visible transition layer.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.2f), Limit(0.0001f, 10.0f, 0.001f), EditorDisplay(\"Exponential Height Fog\")")
float FogHeightFalloff = 0.2f;
@@ -55,7 +55,7 @@ public:
float StartDistance = 0.0f;
/// <summary>
/// Scene elements past this distance will not have fog applied. This is useful for excluding skyboxes which already have fog baked in.
/// Scene elements past this distance will not have fog applied. This is useful for excluding skyboxes which already have fog baked in. Setting this value to 0 disables it.
/// </summary>
API_FIELD(Attributes="EditorOrder(60), DefaultValue(0.0f), Limit(0), EditorDisplay(\"Exponential Height Fog\")")
float FogCutoffDistance = 0.0f;
@@ -111,7 +111,7 @@ public:
Color VolumetricFogAlbedo = Color::White;
/// <summary>
/// Light emitted by height fog. This is a density so more light is emitted the further you are looking through the fog.
/// Light emitted by height fog. This is a density value so more light is emitted the further you are looking through the fog.
/// In most cases using a Skylight is a better choice, however, it may be useful in certain scenarios.
/// </summary>
API_FIELD(Attributes="EditorOrder(330), DefaultValue(typeof(Color), \"0,0,0,1\"), EditorDisplay(\"Volumetric Fog\", \"Emissive\")")

View File

@@ -31,19 +31,19 @@ public:
/// <summary>
/// Directional light that is used to simulate the sun.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Sun\")")
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Sky\")")
ScriptingObjectReference<DirectionalLight> SunLight;
/// <summary>
/// The sun disc scale.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(2.0f), EditorDisplay(\"Sun\"), Limit(0, 100, 0.01f)")
API_FIELD(Attributes="EditorOrder(20), DefaultValue(2.0f), EditorDisplay(\"Sky\"), Limit(0, 100, 0.01f)")
float SunDiscScale = 2.0f;
/// <summary>
/// The sun power.
/// </summary>
API_FIELD(Attributes="EditorOrder(30), DefaultValue(8.0f), EditorDisplay(\"Sun\"), Limit(0, 1000, 0.01f)")
API_FIELD(Attributes="EditorOrder(30), DefaultValue(8.0f), EditorDisplay(\"Sky\"), Limit(0, 1000, 0.01f)")
float SunPower = 8.0f;
private:

View File

@@ -42,24 +42,7 @@ public:
/// <summary>
/// Field for assigning new script to transfer data to.
/// </summary>
API_PROPERTY() void SetReferenceScript(const ScriptingObjectReference<Script>& value)
{
_referenceScript = value;
if (Data.IsEmpty())
return;
rapidjson_flax::Document document;
document.Parse(Data.ToStringAnsi().GetText());
auto modifier = Cache::ISerializeModifier.Get();
_referenceScript->Deserialize(document, modifier.Value);
DeleteObject();
}
API_PROPERTY() void SetReferenceScript(const ScriptingObjectReference<Script>& value);
};
inline MissingScript::MissingScript(const SpawnParams& params)
: Script(params)
{
}
#endif

View File

@@ -1064,6 +1064,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
}
}
}
prefabSyncData.InitNewObjects();
}
// /\ all above this has to be done on an any thread
@@ -1108,7 +1109,7 @@ bool LevelImpl::saveScene(Scene* scene)
bool LevelImpl::saveScene(Scene* scene, const String& path)
{
ASSERT(scene);
ASSERT(scene && EnumHasNoneFlags(scene->Flags, ObjectFlags::WasMarkedToDelete));
auto sceneId = scene->GetID();
LOG(Info, "Saving scene {0} to \'{1}\'", scene->GetName(), path);

View File

@@ -253,7 +253,7 @@ void PrefabInstanceData::SerializePrefabInstances(PrefabInstancesData& prefabIns
for (int32 i = 0; i < sceneObjects->Count(); i++)
{
SceneObject* obj = sceneObjects.Value->At(i);
instance.PrefabInstanceIdToDataIndex.Add(obj->GetSceneObjectId(), i);
instance.PrefabInstanceIdToDataIndex[obj->GetSceneObjectId()] = i;
}
}
tmpBuffer.Clear();
@@ -313,15 +313,13 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
continue;
}
modifier.Value->IdsMapping.Add(obj->GetPrefabObjectID(), obj->GetSceneObjectId());
modifier.Value->IdsMapping[obj->GetPrefabObjectID()] = obj->GetSceneObjectId();
}
}
// Generate new IDs for the added objects (objects in prefab has to have a unique Ids, other than the targetActor instance objects to prevent Id collisions)
for (int32 i = 0; i < newPrefabObjectIds.Count(); i++)
{
modifier->IdsMapping.Add(newPrefabObjectIds[i], Guid::New());
}
modifier->IdsMapping[newPrefabObjectIds[i]] = Guid::New();
// Create new objects added to prefab
int32 deserializeSceneObjectIndex = sceneObjects->Count();
@@ -786,7 +784,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
}
// Cache connection for fast lookup
diffPrefabObjectIdToDataIndex.Add(obj->GetPrefabObjectID(), i);
diffPrefabObjectIdToDataIndex[obj->GetPrefabObjectID()] = i;
// Strip unwanted data
data.RemoveMember("ID");
@@ -796,7 +794,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
else
{
// Object if a new thing
newPrefabInstanceIdToDataIndex.Add(obj->GetSceneObjectId(), i);
newPrefabInstanceIdToDataIndex[obj->GetSceneObjectId()] = i;
}
}
@@ -836,8 +834,8 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
for (auto i = newPrefabInstanceIdToDataIndex.Begin(); i.IsNotEnd(); ++i)
{
const auto prefabObjectId = Guid::New();
newPrefabInstanceIdToPrefabObjectId.Add(i->Key, prefabObjectId);
modifier->IdsMapping.Add(i->Key, prefabObjectId);
newPrefabInstanceIdToPrefabObjectId[i->Key] = prefabObjectId;
modifier->IdsMapping[i->Key] = prefabObjectId;
}
// Add inverse IDs mapping to link added objects and references inside them to the prefab objects

View File

@@ -374,13 +374,13 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
if (targetActor->HasParent())
{
// Unlink from parent actor
objectInstanceIdToPrefabObjectId.Add(targetActor->GetParent()->GetID(), Guid::Empty);
objectInstanceIdToPrefabObjectId[targetActor->GetParent()->GetID()] = Guid::Empty;
}
for (int32 i = 0; i < sceneObjects->Count(); i++)
{
// Generate new IDs for the prefab objects (other than reference instance used to create prefab)
const SceneObject* obj = sceneObjects->At(i);
objectInstanceIdToPrefabObjectId.Add(obj->GetSceneObjectId(), Guid::New());
objectInstanceIdToPrefabObjectId[obj->GetSceneObjectId()] = Guid::New();
}
{
// Parse json to DOM document

View File

@@ -1,7 +1,6 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "SceneObjectsFactory.h"
#include "Components/MissingScript.h"
#include "Engine/Level/Actor.h"
#include "Engine/Level/Prefabs/Prefab.h"
#include "Engine/Content/Content.h"
@@ -17,11 +16,41 @@
#if !BUILD_RELEASE || USE_EDITOR
#include "Engine/Level/Level.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Level/Components/MissingScript.h"
#endif
#if USE_EDITOR
MissingScript::MissingScript(const SpawnParams& params)
: Script(params)
{
}
void MissingScript::SetReferenceScript(const ScriptingObjectReference<Script>& value)
{
if (_referenceScript == value)
return;
_referenceScript = value;
if (Data.IsEmpty() || !_referenceScript)
return;
rapidjson_flax::Document document;
document.Parse(Data.ToStringAnsi().GetText());
document.RemoveMember("ParentID"); // Prevent changing parent
auto modifier = Cache::ISerializeModifier.Get();
const auto idsMapping = Scripting::ObjectsLookupIdMapping.Get();
if (idsMapping)
modifier->IdsMapping = *idsMapping;
_referenceScript->Deserialize(document, modifier.Value);
DeleteObject();
}
#endif
SceneObjectsFactory::Context::Context(ISerializeModifier* modifier)
: Modifier(modifier)
{
// Override the main thread value to not create it again in GetModifier() if called from the same thread
Modifiers.Set(modifier);
}
SceneObjectsFactory::Context::~Context()
@@ -31,7 +60,11 @@ SceneObjectsFactory::Context::~Context()
Array<ISerializeModifier*, FixedAllocation<PLATFORM_THREADS_LIMIT>> modifiers;
Modifiers.GetValues(modifiers);
for (ISerializeModifier* e : modifiers)
{
if (e == Modifier)
continue;
Cache::ISerializeModifier.Put(e);
}
}
}
@@ -57,15 +90,15 @@ ISerializeModifier* SceneObjectsFactory::Context::GetModifier()
return modifier;
}
void SceneObjectsFactory::Context::SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier)
void SceneObjectsFactory::Context::SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier) const
{
int32 instanceIndex;
if (ObjectToInstance.TryGet(obj->GetID(), instanceIndex) && instanceIndex != modifier->CurrentInstance)
{
// Apply the current prefab instance objects ids table to resolve references inside a prefab properly
modifier->CurrentInstance = instanceIndex;
auto& instance = Instances[instanceIndex];
for (auto& e : instance.IdsMapping)
const auto& instance = Instances[instanceIndex];
for (const auto& e : instance.IdsMapping)
modifier->IdsMapping[e.Key] = e.Value;
}
}
@@ -370,6 +403,15 @@ SceneObjectsFactory::PrefabSyncData::PrefabSyncData(Array<SceneObject*>& sceneOb
{
}
void SceneObjectsFactory::PrefabSyncData::InitNewObjects()
{
for (int32 i = 0; i < NewObjects.Count(); i++)
{
SceneObject* obj = SceneObjects[InitialCount + i];
obj->Initialize();
}
}
void SceneObjectsFactory::SetupPrefabInstances(Context& context, const PrefabSyncData& data)
{
PROFILE_CPU_NAMED("SetupPrefabInstances");
@@ -459,9 +501,6 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, Prefab
continue;
const Guid actorParentId = JsonTools::GetGuid(stream, "ParentID");
// Map prefab object id to this actor so the new objects gets added to it
data.Modifier->IdsMapping[actorPrefabObjectId] = actor->GetID();
// Load prefab
auto prefab = Content::LoadAsync<Prefab>(prefabId);
if (prefab == nullptr)
@@ -527,8 +566,12 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, Prefab
if (spawned)
continue;
// Create instance (including all children)
// Map prefab object id to this actor's prefab instance so the new objects gets added to it
context.SetupIdsMapping(actor, data.Modifier);
data.Modifier->IdsMapping[actorPrefabObjectId] = actor->GetID();
Scripting::ObjectsLookupIdMapping.Set(&data.Modifier->IdsMapping);
// Create instance (including all children)
SynchronizeNewPrefabInstance(context, data, prefab, actor, prefabObjectId);
}
}

View File

@@ -33,7 +33,7 @@ public:
~Context();
ISerializeModifier* GetModifier();
void SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier);
void SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier) const;
};
/// <summary>
@@ -78,6 +78,7 @@ public:
ISerializeModifier* Modifier;
PrefabSyncData(Array<SceneObject*>& sceneObjects, const ISerializable::DeserializeStream& data, ISerializeModifier* modifier);
void InitNewObjects();
private:
struct NewObj

View File

@@ -1067,6 +1067,12 @@ void NavMeshBuilder::Update()
void NavMeshBuilder::Build(Scene* scene, float timeoutMs)
{
if (!scene)
{
LOG(Warning, "Could not generate navmesh without scene.");
return;
}
// Early out if scene is not using navigation
if (scene->Navigation.Volumes.IsEmpty())
{
@@ -1098,6 +1104,12 @@ void NavMeshBuilder::Build(Scene* scene, float timeoutMs)
void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs)
{
if (!scene)
{
LOG(Warning, "Could not generate navmesh without scene.");
return;
}
// Early out if scene is not using navigation
if (scene->Navigation.Volumes.IsEmpty())
{

View File

@@ -51,6 +51,33 @@
#include <android/window.h>
#include <android/versioning.h>
#if CRASH_LOG_ENABLE
#include <unwind.h>
#include <cxxabi.h>
struct AndroidBacktraceState
{
void** Current;
void** End;
};
_Unwind_Reason_Code AndroidUnwindCallback(struct _Unwind_Context* context, void* arg)
{
AndroidBacktraceState* state = (AndroidBacktraceState*)arg;
uintptr_t pc = _Unwind_GetIP(context);
if (pc)
{
if (state->Current == state->End)
return _URC_END_OF_STACK;
else
*state->Current++ = reinterpret_cast<void*>(pc);
}
return _URC_NO_REASON;
}
#endif
struct AndroidKeyEventType
{
uint32_t KeyCode;
@@ -1066,4 +1093,45 @@ void* AndroidPlatform::GetProcAddress(void* handle, const char* symbol)
return dlsym(handle, symbol);
}
Array<AndroidPlatform::StackFrame> AndroidPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context)
{
Array<StackFrame> result;
#if CRASH_LOG_ENABLE
// Reference: https://stackoverflow.com/questions/8115192/android-ndk-getting-the-backtrace
void* callstack[120];
skipCount = Math::Min<int32>(skipCount, ARRAY_COUNT(callstack));
int32 maxCount = Math::Min<int32>(ARRAY_COUNT(callstack), skipCount + maxDepth);
AndroidBacktraceState state;
state.Current = callstack;
state.End = callstack + maxCount;
_Unwind_Backtrace(AndroidUnwindCallback, &state);
int32 count = (int32)(state.Current - callstack);
int32 useCount = count - skipCount;
if (useCount > 0)
{
result.Resize(useCount);
for (int32 i = 0; i < useCount; i++)
{
StackFrame& frame = result[i];
frame.ProgramCounter = callstack[skipCount + i];
frame.ModuleName[0] = 0;
frame.FileName[0] = 0;
frame.LineNumber = 0;
Dl_info info;
const char* symbol = "";
if (dladdr(frame.ProgramCounter, &info) && info.dli_sname)
symbol = info.dli_sname;
int status = 0;
char* demangled = __cxxabiv1::__cxa_demangle(symbol, 0, 0, &status);
int32 nameLen = Math::Min<int32>(StringUtils::Length(demangled), ARRAY_COUNT(frame.FunctionName) - 1);
Platform::MemoryCopy(frame.FunctionName, demangled, nameLen);
frame.FunctionName[nameLen] = 0;
if (demangled)
free(demangled);
}
}
#endif
return result;
}
#endif

View File

@@ -132,6 +132,7 @@ public:
static void* LoadLibrary(const Char* filename);
static void FreeLibrary(void* handle);
static void* GetProcAddress(void* handle, const char* symbol);
static Array<StackFrame, HeapAllocation> GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr);
};
#endif

View File

@@ -43,6 +43,7 @@
#include <dlfcn.h>
#if CRASH_LOG_ENABLE
#include <execinfo.h>
#include <cxxabi.h>
#endif
CPUInfo Cpu;
@@ -502,20 +503,42 @@ Array<ApplePlatform::StackFrame> ApplePlatform::GetStackFrames(int32 skipCount,
{
char** names = backtrace_symbols(callstack + skipCount, useCount);
result.Resize(useCount);
Array<StringAnsi> parts;
int32 len;
#define COPY_STR(str, dst) \
len = Math::Min<int32>(str.Length(), ARRAY_COUNT(frame.dst) - 1); \
Platform::MemoryCopy(frame.dst, str.Get(), len); \
frame.dst[len] = 0
for (int32 i = 0; i < useCount; i++)
{
char* name = names[i];
const StringAnsi name(names[i]);
StackFrame& frame = result[i];
frame.ProgramCounter = callstack[skipCount + i];
frame.ModuleName[0] = 0;
frame.FileName[0] = 0;
frame.LineNumber = 0;
int32 nameLen = Math::Min<int32>(StringUtils::Length(name), ARRAY_COUNT(frame.FunctionName) - 1);
Platform::MemoryCopy(frame.FunctionName, name, nameLen);
frame.FunctionName[nameLen] = 0;
// Decode name
parts.Clear();
name.Split(' ', parts);
if (parts.Count() == 6)
{
COPY_STR(parts[1], ModuleName);
const StringAnsiView toDemangle(parts[3]);
int status = 0;
char* demangled = __cxxabiv1::__cxa_demangle(*toDemangle, 0, 0, &status);
const StringAnsiView toCopy = demangled && status == 0 ? StringAnsiView(demangled) : StringAnsiView(toDemangle);
COPY_STR(toCopy, FunctionName);
if (demangled)
free(demangled);
}
else
{
COPY_STR(name, FunctionName);
}
}
free(names);
#undef COPY_STR
}
#endif
return result;

View File

@@ -633,14 +633,21 @@ String PlatformBase::GetStackTrace(int32 skipCount, int32 maxDepth, void* contex
for (const auto& frame : stackFrames)
{
StringAsUTF16<ARRAY_COUNT(StackFrame::FunctionName)> functionName(frame.FunctionName);
const StringView functionNameStr(functionName.Get());
if (StringUtils::Length(frame.FileName) != 0)
{
StringAsUTF16<ARRAY_COUNT(StackFrame::FileName)> fileName(frame.FileName);
result.AppendFormat(TEXT(" at {0}() in {1}:line {2}\n"), functionName.Get(), fileName.Get(), frame.LineNumber);
result.Append(TEXT(" at ")).Append(functionNameStr);
if (!functionNameStr.EndsWith(')'))
result.Append(TEXT("()"));
result.AppendFormat(TEXT("in {0}:line {1}\n"),fileName.Get(), frame.LineNumber);
}
else if (StringUtils::Length(frame.FunctionName) != 0)
{
result.AppendFormat(TEXT(" at {0}()\n"), functionName.Get());
result.Append(TEXT(" at ")).Append(functionNameStr);
if (!functionNameStr.EndsWith(')'))
result.Append(TEXT("()"));
result.Append('\n');
}
else
{

View File

@@ -50,6 +50,10 @@
#include <pwd.h>
#include <inttypes.h>
#include <dlfcn.h>
#if CRASH_LOG_ENABLE
#include <signal.h>
#include <execinfo.h>
#endif
CPUInfo UnixCpu;
int ClockSource;
@@ -1868,6 +1872,46 @@ void LinuxPlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int3
millisecond = time.tv_usec / 1000;
}
#if !BUILD_RELEASE
bool LinuxPlatform::IsDebuggerPresent()
{
static int32 CachedState = -1;
if (CachedState == -1)
{
CachedState = 0;
// Reference: https://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
char buf[4096];
const int status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1)
return false;
const ssize_t num_read = read(status_fd, buf, sizeof(buf) - 1);
close(status_fd);
if (num_read <= 0)
return false;
buf[num_read] = '\0';
constexpr char tracerPidString[] = "TracerPid:";
const auto tracer_pid_ptr = strstr(buf, tracerPidString);
if (!tracer_pid_ptr)
return false;
for (const char* characterPtr = tracer_pid_ptr + sizeof(tracerPidString) - 1; characterPtr <= buf + num_read; ++characterPtr)
{
if (StringUtils::IsWhitespace(*characterPtr))
continue;
else
{
if (StringUtils::IsDigit(*characterPtr) && *characterPtr != '0')
CachedState = 1;
return CachedState == 1;
}
}
}
return CachedState == 1;
}
#endif
bool LinuxPlatform::Init()
{
if (PlatformBase::Init())
@@ -2992,4 +3036,36 @@ void* LinuxPlatform::GetProcAddress(void* handle, const char* symbol)
return dlsym(handle, symbol);
}
Array<LinuxPlatform::StackFrame> LinuxPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context)
{
Array<StackFrame> result;
#if CRASH_LOG_ENABLE
void* callstack[120];
skipCount = Math::Min<int32>(skipCount, ARRAY_COUNT(callstack));
int32 maxCount = Math::Min<int32>(ARRAY_COUNT(callstack), skipCount + maxDepth);
int32 count = backtrace(callstack, maxCount);
int32 useCount = count - skipCount;
if (useCount > 0)
{
char** names = backtrace_symbols(callstack + skipCount, useCount);
result.Resize(useCount);
for (int32 i = 0; i < useCount; i++)
{
char* name = names[i];
StackFrame& frame = result[i];
frame.ProgramCounter = callstack[skipCount + i];
frame.ModuleName[0] = 0;
frame.FileName[0] = 0;
frame.LineNumber = 0;
int32 nameLen = Math::Min<int32>(StringUtils::Length(name), ARRAY_COUNT(frame.FunctionName) - 1);
Platform::MemoryCopy(frame.FunctionName, name, nameLen);
frame.FunctionName[nameLen] = 0;
}
free(names);
}
#endif
return result;
}
#endif

View File

@@ -114,6 +114,9 @@ public:
}
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
#if !BUILD_RELEASE
static bool IsDebuggerPresent();
#endif
static bool Init();
static void BeforeRun();
static void Tick();
@@ -143,6 +146,7 @@ public:
static void* LoadLibrary(const Char* filename);
static void FreeLibrary(void* handle);
static void* GetProcAddress(void* handle, const char* symbol);
static Array<StackFrame, HeapAllocation> GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr);
};
#endif

View File

@@ -565,7 +565,10 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
_vertexBuffer->Data.EnsureCapacity(_dirtyObjectsBuffer.Count() * 6 * sizeof(AtlasTileVertex));
for (void* actorObject : _dirtyObjectsBuffer)
{
const auto& object = ((const Dictionary<Actor*, GlobalSurfaceAtlasObject>&)surfaceAtlasData.Objects)[actorObject];
const GlobalSurfaceAtlasObject* objectPtr = surfaceAtlasData.Objects.TryGet(actorObject);
if (!objectPtr)
continue;
const GlobalSurfaceAtlasObject& object = *objectPtr;
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
{
auto* tile = object.Tiles[tileIndex];
@@ -587,7 +590,10 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
int32 tilesDrawn = 0;
for (void* actorObject : _dirtyObjectsBuffer)
{
const auto& object = ((const Dictionary<Actor*, GlobalSurfaceAtlasObject>&)surfaceAtlasData.Objects)[actorObject];
const GlobalSurfaceAtlasObject* objectPtr = surfaceAtlasData.Objects.TryGet(actorObject);
if (!objectPtr)
continue;
const GlobalSurfaceAtlasObject& object = *objectPtr;
// Clear draw calls list
renderContextTiles.List->DrawCalls.Clear();

View File

@@ -693,6 +693,14 @@ void BinaryModule::Destroy(bool isReloading)
}
}
// Remove any scripting events
for (auto i = ScriptingEvents::EventsTable.Begin(); i.IsNotEnd(); ++i)
{
const ScriptingTypeHandle type = i->Key.First;
if (type.Module == this)
ScriptingEvents::EventsTable.Remove(i);
}
// Unregister
GetModules().RemoveKeepOrder(this);
}

View File

@@ -1026,30 +1026,28 @@ bool Scripting::IsTypeFromGameScripts(MClass* type)
void Scripting::RegisterObject(ScriptingObject* obj)
{
const Guid id = obj->GetID();
ScopeLock lock(_objectsLocker);
//ASSERT(!_objectsDictionary.ContainsValue(obj));
#if ENABLE_ASSERTION
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
ScriptingObjectData other;
if (_objectsDictionary.TryGet(obj->GetID(), other))
if (_objectsDictionary.TryGet(id, other))
#else
ScriptingObject* other;
if (_objectsDictionary.TryGet(obj->GetID(), other))
if (_objectsDictionary.TryGet(id, other))
#endif
{
// Something went wrong...
LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName()));
_objectsDictionary.Remove(obj->GetID());
LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", id, obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName()));
}
#else
ASSERT(!_objectsDictionary.ContainsKey(obj->_id));
#endif
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
LOG(Info, "[RegisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName));
#endif
_objectsDictionary.Add(obj->GetID(), obj);
_objectsDictionary[id] = obj;
}
void Scripting::UnregisterObject(ScriptingObject* obj)

View File

@@ -487,9 +487,12 @@ namespace FlaxEngine.GUI
public void UpdateTransform()
{
// Actual pivot and negative pivot
Float2.Multiply(ref _pivot, ref _bounds.Size, out var v1);
Float2.Negate(ref v1, out var v2);
Float2.Add(ref v1, ref _bounds.Location, out v1);
//Float2.Multiply(ref _pivot, ref _bounds.Size, out var v1);
//Float2.Negate(ref v1, out var v2);
//Float2.Add(ref v1, ref _bounds.Location, out v1);
var v1 = _pivot * _bounds.Size;
var v2 = -v1;
v1 += _bounds.Location;
// ------ Matrix3x3 based version:
@@ -518,18 +521,42 @@ namespace FlaxEngine.GUI
// ------ Matrix2x2 based version:
// 2D transformation
Matrix2x2.Scale(ref _scale, out Matrix2x2 m1);
Matrix2x2.Shear(ref _shear, out Matrix2x2 m2);
Matrix2x2.Multiply(ref m1, ref m2, out m1);
Matrix2x2.Rotation(Mathf.DegreesToRadians * _rotation, out m2);
Matrix2x2.Multiply(ref m1, ref m2, out m1);
//Matrix2x2.Scale(ref _scale, out Matrix2x2 m1);
//Matrix2x2.Shear(ref _shear, out Matrix2x2 m2);
//Matrix2x2.Multiply(ref m1, ref m2, out m1);
// Scale and Shear
Matrix3x3 m1 = new Matrix3x3
(
_scale.X,
_scale.X * (_shear.Y == 0 ? 0 : (1.0f / Mathf.Tan(Mathf.DegreesToRadians * (90 - Mathf.Clamp(_shear.Y, -89.0f, 89.0f))))),
0,
_scale.Y * (_shear.X == 0 ? 0 : (1.0f / Mathf.Tan(Mathf.DegreesToRadians * (90 - Mathf.Clamp(_shear.X, -89.0f, 89.0f))))),
_scale.Y,
0, 0, 0, 1
);
//Matrix2x2.Rotation(Mathf.DegreesToRadians * _rotation, out m2);
float sin = Mathf.Sin(Mathf.DegreesToRadians * _rotation);
float cos = Mathf.Cos(Mathf.DegreesToRadians * _rotation);
//Matrix2x2.Multiply(ref m1, ref m2, out m1);
m1.M11 = (_scale.X * cos) + (m1.M12 * -sin);
m1.M12 = (_scale.X * sin) + (m1.M12 * cos);
float m21 = (m1.M21 * cos) + (_scale.Y * -sin);
m1.M22 = (m1.M21 * sin) + (_scale.Y * cos);
m1.M21 = m21;
// Mix all the stuff
Matrix3x3.Translation2D(ref v2, out Matrix3x3 m3);
Matrix3x3 m4 = (Matrix3x3)m1;
Matrix3x3.Multiply(ref m3, ref m4, out m3);
Matrix3x3.Translation2D(ref v1, out m4);
Matrix3x3.Multiply(ref m3, ref m4, out _cachedTransform);
//Matrix3x3.Translation2D(ref v2, out Matrix3x3 m3);
//Matrix3x3 m4 = (Matrix3x3)m1;
//Matrix3x3.Multiply(ref m3, ref m4, out m3);
//Matrix3x3.Translation2D(ref v1, out m4);
//Matrix3x3.Multiply(ref m3, ref m4, out _cachedTransform);
m1.M31 = (v2.X * m1.M11) + (v2.Y * m1.M21) + v1.X;
m1.M32 = (v2.X * m1.M12) + (v2.Y * m1.M22) + v1.Y;
_cachedTransform = m1;
// Cache inverted transform
Matrix3x3.Invert(ref _cachedTransform, out _cachedTransformInv);