Merge remote-tracking branch 'origin/master' into 1.6
# Conflicts: # .github/workflows/tests.yml # Source/Engine/Content/JsonAsset.cs
This commit is contained in:
BIN
Content/Shaders/DebugDraw.flax
(Stored with Git LFS)
BIN
Content/Shaders/DebugDraw.flax
(Stored with Git LFS)
Binary file not shown.
@@ -45,7 +45,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
var upperRightCell = new VerticalPanel
|
var upperRightCell = new VerticalPanel
|
||||||
{
|
{
|
||||||
ClipChildren = false,
|
ClipChildren = false,
|
||||||
Pivot = new Float2(0.0f, 0.0f),
|
Pivot = new Float2(0.00001f, 0.0f),
|
||||||
Offset = new Float2(-labelsWidth, 0),
|
Offset = new Float2(-labelsWidth, 0),
|
||||||
Rotation = -90,
|
Rotation = -90,
|
||||||
Spacing = 0,
|
Spacing = 0,
|
||||||
|
|||||||
@@ -145,7 +145,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No localization so initialize with empty table
|
// No localization so initialize with empty table
|
||||||
var path = Path.Combine(Path.Combine(Path.GetDirectoryName(GameSettings.Load().Localization.Path), "Localization", culture.Name + ".json"));
|
var folder = Path.Combine(Path.GetDirectoryName(GameSettings.Load().Localization.Path), "Localization");
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
var path = Path.Combine(Path.Combine(folder, culture.Name + ".json"));
|
||||||
var table = FlaxEngine.Content.CreateVirtualAsset<LocalizedStringTable>();
|
var table = FlaxEngine.Content.CreateVirtualAsset<LocalizedStringTable>();
|
||||||
table.Locale = culture.Name;
|
table.Locale = culture.Name;
|
||||||
if (!table.Save(path))
|
if (!table.Save(path))
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using FlaxEditor.Content;
|
|||||||
using FlaxEditor.Content.Import;
|
using FlaxEditor.Content.Import;
|
||||||
using FlaxEditor.Content.Settings;
|
using FlaxEditor.Content.Settings;
|
||||||
using FlaxEditor.Content.Thumbnails;
|
using FlaxEditor.Content.Thumbnails;
|
||||||
using FlaxEditor.GUI;
|
|
||||||
using FlaxEditor.Modules;
|
using FlaxEditor.Modules;
|
||||||
using FlaxEditor.Modules.SourceCodeEditing;
|
using FlaxEditor.Modules.SourceCodeEditing;
|
||||||
using FlaxEditor.Options;
|
using FlaxEditor.Options;
|
||||||
@@ -314,6 +313,7 @@ namespace FlaxEditor
|
|||||||
_areModulesInited = true;
|
_areModulesInited = true;
|
||||||
|
|
||||||
// Preload initial scene asset
|
// Preload initial scene asset
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var startupSceneMode = Options.Options.General.StartupSceneMode;
|
var startupSceneMode = Options.Options.General.StartupSceneMode;
|
||||||
if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene))
|
if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene))
|
||||||
@@ -330,12 +330,23 @@ namespace FlaxEditor
|
|||||||
}
|
}
|
||||||
case GeneralOptions.StartupSceneModes.LastOpened:
|
case GeneralOptions.StartupSceneModes.LastOpened:
|
||||||
{
|
{
|
||||||
if (ProjectCache.TryGetCustomData(ProjectDataLastScene, out var lastSceneIdName) && Guid.TryParse(lastSceneIdName, out var lastSceneId))
|
if (ProjectCache.TryGetCustomData(ProjectDataLastScene, out var lastSceneIdName))
|
||||||
Internal_LoadAsset(ref lastSceneId);
|
{
|
||||||
|
var lastScenes = JsonSerializer.Deserialize<Guid[]>(lastSceneIdName);
|
||||||
|
foreach (var scene in lastScenes)
|
||||||
|
{
|
||||||
|
var lastScene = scene;
|
||||||
|
Internal_LoadAsset(ref lastScene);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Ignore errors
|
||||||
|
}
|
||||||
|
|
||||||
InitializationStart?.Invoke();
|
InitializationStart?.Invoke();
|
||||||
|
|
||||||
@@ -408,58 +419,69 @@ namespace FlaxEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load scene
|
// Load scene
|
||||||
|
try
|
||||||
// scene cmd line argument
|
|
||||||
var scene = ContentDatabase.Find(_startupSceneCmdLine);
|
|
||||||
if (scene is SceneItem)
|
|
||||||
{
|
{
|
||||||
Editor.Log("Loading scene specified in command line");
|
// Scene cmd line argument
|
||||||
Scene.OpenScene(_startupSceneCmdLine);
|
var scene = ContentDatabase.Find(_startupSceneCmdLine);
|
||||||
return;
|
if (scene is SceneItem)
|
||||||
}
|
|
||||||
|
|
||||||
// if no scene cmd line argument is provided
|
|
||||||
var startupSceneMode = Options.Options.General.StartupSceneMode;
|
|
||||||
if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene))
|
|
||||||
{
|
|
||||||
// Fallback to default project scene if nothing saved in the cache
|
|
||||||
startupSceneMode = GeneralOptions.StartupSceneModes.ProjectDefault;
|
|
||||||
}
|
|
||||||
switch (startupSceneMode)
|
|
||||||
{
|
|
||||||
case GeneralOptions.StartupSceneModes.ProjectDefault:
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(GameProject.DefaultScene))
|
|
||||||
break;
|
|
||||||
JsonSerializer.ParseID(GameProject.DefaultScene, out var defaultSceneId);
|
|
||||||
var defaultScene = ContentDatabase.Find(defaultSceneId);
|
|
||||||
if (defaultScene is SceneItem)
|
|
||||||
{
|
{
|
||||||
Editor.Log("Loading default project scene");
|
Editor.Log("Loading scene specified in command line");
|
||||||
Scene.OpenScene(defaultSceneId);
|
Scene.OpenScene(_startupSceneCmdLine);
|
||||||
|
return;
|
||||||
// Use spawn point
|
|
||||||
Windows.EditWin.Viewport.ViewRay = GameProject.DefaultSceneSpawn;
|
|
||||||
}
|
}
|
||||||
break;
|
var startupSceneMode = Options.Options.General.StartupSceneMode;
|
||||||
}
|
if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene))
|
||||||
case GeneralOptions.StartupSceneModes.LastOpened:
|
|
||||||
{
|
|
||||||
if (ProjectCache.TryGetCustomData(ProjectDataLastScene, out var lastSceneIdName) && Guid.TryParse(lastSceneIdName, out var lastSceneId))
|
|
||||||
{
|
{
|
||||||
var lastScene = ContentDatabase.Find(lastSceneId);
|
// Fallback to default project scene if nothing saved in the cache
|
||||||
if (lastScene is SceneItem)
|
startupSceneMode = GeneralOptions.StartupSceneModes.ProjectDefault;
|
||||||
|
}
|
||||||
|
switch (startupSceneMode)
|
||||||
|
{
|
||||||
|
case GeneralOptions.StartupSceneModes.ProjectDefault:
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(GameProject.DefaultScene))
|
||||||
|
break;
|
||||||
|
JsonSerializer.ParseID(GameProject.DefaultScene, out var defaultSceneId);
|
||||||
|
var defaultScene = ContentDatabase.Find(defaultSceneId);
|
||||||
|
if (defaultScene is SceneItem)
|
||||||
{
|
{
|
||||||
Editor.Log("Loading last opened scene");
|
Editor.Log("Loading default project scene");
|
||||||
Scene.OpenScene(lastSceneId);
|
Scene.OpenScene(defaultSceneId);
|
||||||
|
|
||||||
|
// Use spawn point
|
||||||
|
Windows.EditWin.Viewport.ViewRay = GameProject.DefaultSceneSpawn;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GeneralOptions.StartupSceneModes.LastOpened:
|
||||||
|
{
|
||||||
|
if (ProjectCache.TryGetCustomData(ProjectDataLastScene, out var lastSceneIdName))
|
||||||
|
{
|
||||||
|
var lastScenes = JsonSerializer.Deserialize<Guid[]>(lastSceneIdName);
|
||||||
|
foreach (var sceneId in lastScenes)
|
||||||
|
{
|
||||||
|
var lastScene = ContentDatabase.Find(sceneId);
|
||||||
|
if (!(lastScene is SceneItem))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Editor.Log($"Loading last opened scene: {lastScene.ShortName}");
|
||||||
|
if (sceneId == lastScenes[0])
|
||||||
|
Scene.OpenScene(sceneId);
|
||||||
|
else
|
||||||
|
Level.LoadSceneAsync(sceneId);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore view
|
// Restore view
|
||||||
if (ProjectCache.TryGetCustomData(ProjectDataLastSceneSpawn, out var lastSceneSpawnName))
|
if (ProjectCache.TryGetCustomData(ProjectDataLastSceneSpawn, out var lastSceneSpawnName))
|
||||||
Windows.EditWin.Viewport.ViewRay = JsonSerializer.Deserialize<Ray>(lastSceneSpawnName);
|
Windows.EditWin.Viewport.ViewRay = JsonSerializer.Deserialize<Ray>(lastSceneSpawnName);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Ignore errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,11 +691,14 @@ namespace FlaxEditor
|
|||||||
// Start exit
|
// Start exit
|
||||||
StateMachine.GoToState<ClosingState>();
|
StateMachine.GoToState<ClosingState>();
|
||||||
|
|
||||||
// Cache last opened scene
|
// Cache last opened scenes
|
||||||
{
|
{
|
||||||
var lastSceneId = Level.ScenesCount > 0 ? Level.Scenes[0].ID : Guid.Empty;
|
var lastScenes = Level.Scenes;
|
||||||
|
var lastSceneIds = new Guid[lastScenes.Length];
|
||||||
|
for (int i = 0; i < lastScenes.Length; i++)
|
||||||
|
lastSceneIds[i] = lastScenes[i].ID;
|
||||||
var lastSceneSpawn = Windows.EditWin.Viewport.ViewRay;
|
var lastSceneSpawn = Windows.EditWin.Viewport.ViewRay;
|
||||||
ProjectCache.SetCustomData(ProjectDataLastScene, lastSceneId.ToString());
|
ProjectCache.SetCustomData(ProjectDataLastScene, JsonSerializer.Serialize(lastSceneIds));
|
||||||
ProjectCache.SetCustomData(ProjectDataLastSceneSpawn, JsonSerializer.Serialize(lastSceneSpawn));
|
ProjectCache.SetCustomData(ProjectDataLastSceneSpawn, JsonSerializer.Serialize(lastSceneSpawn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -626,7 +626,16 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
{
|
{
|
||||||
var item = _dragAssets.Objects[i];
|
var item = _dragAssets.Objects[i];
|
||||||
var actor = item.OnEditorDrop(this);
|
var actor = item.OnEditorDrop(this);
|
||||||
actor.StaticFlags = spawnParent.StaticFlags;
|
if (spawnParent.GetType() != typeof(Scene))
|
||||||
|
{
|
||||||
|
// Set all Actors static flags to match parents
|
||||||
|
List<Actor> childActors = new List<Actor>();
|
||||||
|
GetActorsTree(childActors, actor);
|
||||||
|
foreach (var child in childActors)
|
||||||
|
{
|
||||||
|
child.StaticFlags = spawnParent.StaticFlags;
|
||||||
|
}
|
||||||
|
}
|
||||||
actor.Name = item.ShortName;
|
actor.Name = item.ShortName;
|
||||||
actor.Transform = spawnParent.Transform;
|
actor.Transform = spawnParent.Transform;
|
||||||
ActorNode.Root.Spawn(actor, spawnParent);
|
ActorNode.Root.Spawn(actor, spawnParent);
|
||||||
@@ -667,6 +676,16 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GetActorsTree(List<Actor> list, Actor a)
|
||||||
|
{
|
||||||
|
list.Add(a);
|
||||||
|
int cnt = a.ChildrenCount;
|
||||||
|
for (int i = 0; i < cnt; i++)
|
||||||
|
{
|
||||||
|
GetActorsTree(list, a.GetChild(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool ValidateDragActor(ActorNode actorNode)
|
private bool ValidateDragActor(ActorNode actorNode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -730,12 +730,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0),
|
||||||
|
NodeElementArchetype.Factory.Input(0, "Min", true, typeof(float), 1, 0),
|
||||||
NodeElementArchetype.Factory.Text(0, 0, "Min", 30.0f, 18.0f),
|
NodeElementArchetype.Factory.Input(1, "Max", true, typeof(float), 2, 1),
|
||||||
NodeElementArchetype.Factory.Float(30, 0, 0),
|
|
||||||
|
|
||||||
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY, "Max", 30.0f, 18.0f),
|
|
||||||
NodeElementArchetype.Factory.Float(30, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
@@ -753,14 +749,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float2), 0),
|
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float2), 0),
|
||||||
|
NodeElementArchetype.Factory.Input(0, "Min", true, typeof(Float2), 1, 0),
|
||||||
NodeElementArchetype.Factory.Text(0, 0, "Min", 30.0f, 18.0f),
|
NodeElementArchetype.Factory.Input(1, "Max", true, typeof(Float2), 2, 1),
|
||||||
NodeElementArchetype.Factory.Vector_X(30, 0, 0),
|
|
||||||
NodeElementArchetype.Factory.Vector_Y(83, 0, 0),
|
|
||||||
|
|
||||||
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY, "Max", 30.0f, 18.0f),
|
|
||||||
NodeElementArchetype.Factory.Vector_X(30, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
NodeElementArchetype.Factory.Vector_Y(83, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
@@ -778,16 +768,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0),
|
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0),
|
||||||
|
NodeElementArchetype.Factory.Input(0, "Min", true, typeof(Float3), 1, 0),
|
||||||
NodeElementArchetype.Factory.Text(0, 0, "Min", 30.0f, 18.0f),
|
NodeElementArchetype.Factory.Input(1, "Max", true, typeof(Float3), 2, 1),
|
||||||
NodeElementArchetype.Factory.Vector_X(30, 0, 0),
|
|
||||||
NodeElementArchetype.Factory.Vector_Y(83, 0, 0),
|
|
||||||
NodeElementArchetype.Factory.Vector_Z(136, 0, 0),
|
|
||||||
|
|
||||||
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY, "Max", 30.0f, 18.0f),
|
|
||||||
NodeElementArchetype.Factory.Vector_X(30, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
NodeElementArchetype.Factory.Vector_Y(83, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
NodeElementArchetype.Factory.Vector_Z(136, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
@@ -805,18 +787,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float4), 0),
|
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float4), 0),
|
||||||
|
NodeElementArchetype.Factory.Input(0, "Min", true, typeof(Float4), 1, 0),
|
||||||
NodeElementArchetype.Factory.Text(0, 0, "Min", 30.0f, 18.0f),
|
NodeElementArchetype.Factory.Input(1, "Max", true, typeof(Float4), 2, 1),
|
||||||
NodeElementArchetype.Factory.Vector_X(30, 0, 0),
|
|
||||||
NodeElementArchetype.Factory.Vector_Y(83, 0, 0),
|
|
||||||
NodeElementArchetype.Factory.Vector_Z(136, 0, 0),
|
|
||||||
NodeElementArchetype.Factory.Vector_W(189, 0, 0),
|
|
||||||
|
|
||||||
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY, "Max", 30.0f, 18.0f),
|
|
||||||
NodeElementArchetype.Factory.Vector_X(30, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
NodeElementArchetype.Factory.Vector_Y(83, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
NodeElementArchetype.Factory.Vector_Z(136, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
NodeElementArchetype.Factory.Vector_W(189, Surface.Constants.LayoutOffsetY, 1),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ AssetReference<MaterialInstance> CustomTextureMaterial;
|
|||||||
ModelInstanceEntries InstanceBuffers[static_cast<int32>(IconTypes::MAX)];
|
ModelInstanceEntries InstanceBuffers[static_cast<int32>(IconTypes::MAX)];
|
||||||
Dictionary<ScriptingTypeHandle, IconTypes> ActorTypeToIconType;
|
Dictionary<ScriptingTypeHandle, IconTypes> ActorTypeToIconType;
|
||||||
Dictionary<ScriptingTypeHandle, AssetReference<Texture>> ActorTypeToTexture;
|
Dictionary<ScriptingTypeHandle, AssetReference<Texture>> ActorTypeToTexture;
|
||||||
|
Dictionary<ScriptingObjectReference<Actor>, AssetReference<Texture>> ActorToTexture;
|
||||||
|
Dictionary<AssetReference<Texture>, AssetReference<MaterialBase>> TextureToMaterial;
|
||||||
|
|
||||||
class ViewportIconsRendererService : public EngineService
|
class ViewportIconsRendererService : public EngineService
|
||||||
{
|
{
|
||||||
@@ -103,10 +105,18 @@ void ViewportIconsRenderer::AddActor(Actor* actor)
|
|||||||
actor->GetSceneRendering()->AddViewportIcon(actor);
|
actor->GetSceneRendering()->AddViewportIcon(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewportIconsRenderer::AddActorWithTexture(Actor* actor, Texture* iconTexture)
|
||||||
|
{
|
||||||
|
CHECK(actor && actor->GetScene() && iconTexture);
|
||||||
|
ActorToTexture[actor] = iconTexture;
|
||||||
|
actor->GetSceneRendering()->AddViewportIcon(actor);
|
||||||
|
}
|
||||||
|
|
||||||
void ViewportIconsRenderer::RemoveActor(Actor* actor)
|
void ViewportIconsRenderer::RemoveActor(Actor* actor)
|
||||||
{
|
{
|
||||||
CHECK(actor && actor->GetScene());
|
CHECK(actor && actor->GetScene());
|
||||||
actor->GetSceneRendering()->RemoveViewportIcon(actor);
|
actor->GetSceneRendering()->RemoveViewportIcon(actor);
|
||||||
|
ActorToTexture.Remove(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw)
|
void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw)
|
||||||
@@ -128,7 +138,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
|
|||||||
ScriptingTypeHandle typeHandle = icon->GetTypeHandle();
|
ScriptingTypeHandle typeHandle = icon->GetTypeHandle();
|
||||||
draw.Buffer = nullptr;
|
draw.Buffer = nullptr;
|
||||||
|
|
||||||
if (ActorTypeToTexture.TryGet(typeHandle, texture))
|
if (ActorToTexture.TryGet(icon, texture) || ActorTypeToTexture.TryGet(typeHandle, texture))
|
||||||
{
|
{
|
||||||
// Use custom texture
|
// Use custom texture
|
||||||
draw.Buffer = &InstanceBuffers[static_cast<int32>(IconTypes::CustomTexture)];
|
draw.Buffer = &InstanceBuffers[static_cast<int32>(IconTypes::CustomTexture)];
|
||||||
@@ -137,9 +147,20 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
|
|||||||
// Lazy-init (use in-built icon material with custom texture)
|
// Lazy-init (use in-built icon material with custom texture)
|
||||||
draw.Buffer->Setup(1);
|
draw.Buffer->Setup(1);
|
||||||
draw.Buffer->At(0).ReceiveDecals = false;
|
draw.Buffer->At(0).ReceiveDecals = false;
|
||||||
draw.Buffer->At(0).Material = InstanceBuffers[0][0].Material->CreateVirtualInstance();
|
draw.Buffer->At(0).ShadowsMode = ShadowsCastingMode::None;
|
||||||
}
|
}
|
||||||
draw.Buffer->At(0).Material->SetParameterValue(TEXT("Image"), Variant(texture));
|
|
||||||
|
AssetReference<MaterialBase> material;
|
||||||
|
|
||||||
|
if (!TextureToMaterial.TryGet(texture, material))
|
||||||
|
{
|
||||||
|
// Create custom material per custom texture
|
||||||
|
TextureToMaterial[texture] = InstanceBuffers[0][0].Material->CreateVirtualInstance();
|
||||||
|
TextureToMaterial[texture]->SetParameterValue(TEXT("Image"), Variant(texture));
|
||||||
|
material = TextureToMaterial[texture];
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.Buffer->At(0).Material = material;
|
||||||
}
|
}
|
||||||
else if (ActorTypeToIconType.TryGet(typeHandle, iconType))
|
else if (ActorTypeToIconType.TryGet(typeHandle, iconType))
|
||||||
{
|
{
|
||||||
@@ -170,6 +191,8 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
|
|||||||
Matrix m1, m2, world;
|
Matrix m1, m2, world;
|
||||||
BoundingSphere sphere(actor->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
|
BoundingSphere sphere(actor->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
|
||||||
IconTypes iconType;
|
IconTypes iconType;
|
||||||
|
AssetReference<Texture> texture;
|
||||||
|
|
||||||
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType))
|
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType))
|
||||||
{
|
{
|
||||||
// Create world matrix
|
// Create world matrix
|
||||||
@@ -182,7 +205,39 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
|
|||||||
// Draw icon
|
// Draw icon
|
||||||
GeometryDrawStateData drawState;
|
GeometryDrawStateData drawState;
|
||||||
draw.DrawState = &drawState;
|
draw.DrawState = &drawState;
|
||||||
draw.Buffer = &InstanceBuffers[static_cast<int32>(iconType)];
|
|
||||||
|
// Support custom icons through types, but not onces that were added through actors,
|
||||||
|
// since they cant register while in prefab view anyway
|
||||||
|
if (ActorTypeToTexture.TryGet(actor->GetTypeHandle(), texture))
|
||||||
|
{
|
||||||
|
// Use custom texture
|
||||||
|
draw.Buffer = &InstanceBuffers[static_cast<int32>(IconTypes::CustomTexture)];
|
||||||
|
if (draw.Buffer->Count() == 0)
|
||||||
|
{
|
||||||
|
// Lazy-init (use in-built icon material with custom texture)
|
||||||
|
draw.Buffer->Setup(1);
|
||||||
|
draw.Buffer->At(0).ReceiveDecals = false;
|
||||||
|
draw.Buffer->At(0).ShadowsMode = ShadowsCastingMode::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetReference<MaterialBase> material;
|
||||||
|
|
||||||
|
if (!TextureToMaterial.TryGet(texture, material))
|
||||||
|
{
|
||||||
|
// Create custom material per custom texture
|
||||||
|
TextureToMaterial[texture] = InstanceBuffers[0][0].Material->CreateVirtualInstance();
|
||||||
|
TextureToMaterial[texture]->SetParameterValue(TEXT("Image"), Variant(texture));
|
||||||
|
material = TextureToMaterial[texture];
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.Buffer->At(0).Material = material;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use predefined material
|
||||||
|
draw.Buffer = &InstanceBuffers[static_cast<int32>(iconType)];
|
||||||
|
}
|
||||||
|
|
||||||
draw.World = &world;
|
draw.World = &world;
|
||||||
draw.Bounds = sphere;
|
draw.Bounds = sphere;
|
||||||
QuadModel->Draw(renderContext, draw);
|
QuadModel->Draw(renderContext, draw);
|
||||||
@@ -196,9 +251,10 @@ bool ViewportIconsRendererService::Init()
|
|||||||
{
|
{
|
||||||
QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
|
QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
|
||||||
#define INIT(type, path) \
|
#define INIT(type, path) \
|
||||||
InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \
|
InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \
|
||||||
InstanceBuffers[static_cast<int32>(IconTypes::type)][0].ReceiveDecals = false; \
|
InstanceBuffers[static_cast<int32>(IconTypes::type)][0].ReceiveDecals = false; \
|
||||||
InstanceBuffers[static_cast<int32>(IconTypes::type)][0].Material = Content::LoadAsyncInternal<MaterialInstance>(TEXT(path))
|
InstanceBuffers[static_cast<int32>(IconTypes::type)][0].ShadowsMode = ShadowsCastingMode::None; \
|
||||||
|
InstanceBuffers[static_cast<int32>(IconTypes::type)][0].Material = Content::LoadAsyncInternal<MaterialInstance>(TEXT(path))
|
||||||
INIT(PointLight, "Editor/Icons/PointLight");
|
INIT(PointLight, "Editor/Icons/PointLight");
|
||||||
INIT(DirectionalLight, "Editor/Icons/DirectionalLight");
|
INIT(DirectionalLight, "Editor/Icons/DirectionalLight");
|
||||||
INIT(EnvironmentProbe, "Editor/Icons/EnvironmentProbe");
|
INIT(EnvironmentProbe, "Editor/Icons/EnvironmentProbe");
|
||||||
@@ -236,4 +292,6 @@ void ViewportIconsRendererService::Dispose()
|
|||||||
for (int32 i = 0; i < ARRAY_COUNT(InstanceBuffers); i++)
|
for (int32 i = 0; i < ARRAY_COUNT(InstanceBuffers); i++)
|
||||||
InstanceBuffers[i].Release();
|
InstanceBuffers[i].Release();
|
||||||
ActorTypeToIconType.Clear();
|
ActorTypeToIconType.Clear();
|
||||||
|
ActorToTexture.Clear();
|
||||||
|
TextureToMaterial.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,13 @@ public:
|
|||||||
/// <param name="actor">The actor to register for icon drawing.</param>
|
/// <param name="actor">The actor to register for icon drawing.</param>
|
||||||
API_FUNCTION() static void AddActor(Actor* actor);
|
API_FUNCTION() static void AddActor(Actor* actor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds actor to the viewport icon rendering.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="actor">The actor to register for icon drawing.</param>
|
||||||
|
/// <param name="iconTexture">The icon texture to draw.</param>
|
||||||
|
API_FUNCTION() static void AddActorWithTexture(Actor* actor, Texture* iconTexture);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes actor from the viewport icon rendering.
|
/// Removes actor from the viewport icon rendering.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -126,6 +126,10 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_presenter.NoSelectionText = "Failed to load asset. See log for more. " + ex.Message.Replace('\n', ' ');
|
_presenter.NoSelectionText = "Failed to load asset. See log for more. " + ex.Message.Replace('\n', ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (string.IsNullOrEmpty(dataTypeName))
|
||||||
|
{
|
||||||
|
_presenter.NoSelectionText = "Empty data type.";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_presenter.NoSelectionText = string.Format("Missing type '{0}'.", dataTypeName);
|
_presenter.NoSelectionText = string.Format("Missing type '{0}'.", dataTypeName);
|
||||||
|
|||||||
@@ -489,7 +489,10 @@ namespace FlaxEditor.Windows
|
|||||||
/// <param name="item">The item to delete.</param>
|
/// <param name="item">The item to delete.</param>
|
||||||
public void Delete(ContentItem item)
|
public void Delete(ContentItem item)
|
||||||
{
|
{
|
||||||
Delete(Editor.Instance.Windows.ContentWin.View.Selection);
|
var items = View.Selection;
|
||||||
|
if (items.Count == 0)
|
||||||
|
items = new List<ContentItem>() { item };
|
||||||
|
Delete(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -657,6 +660,11 @@ namespace FlaxEditor.Windows
|
|||||||
throw new ArgumentNullException(nameof(proxy));
|
throw new ArgumentNullException(nameof(proxy));
|
||||||
|
|
||||||
string name = initialName ?? proxy.NewItemName;
|
string name = initialName ?? proxy.NewItemName;
|
||||||
|
|
||||||
|
// If the proxy can not be created in the current folder, then navigate to the content folder
|
||||||
|
if (!proxy.CanCreate(CurrentViewFolder))
|
||||||
|
Navigate(Editor.Instance.ContentDatabase.Game.Content);
|
||||||
|
|
||||||
ContentFolder parentFolder = CurrentViewFolder;
|
ContentFolder parentFolder = CurrentViewFolder;
|
||||||
string parentFolderPath = parentFolder.Path;
|
string parentFolderPath = parentFolder.Path;
|
||||||
|
|
||||||
|
|||||||
@@ -1002,17 +1002,12 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo&
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if ASSETS_LOADING_EXTRA_VERIFICATION
|
#if ASSETS_LOADING_EXTRA_VERIFICATION
|
||||||
|
|
||||||
// Ensure we have valid asset info
|
|
||||||
ASSERT(assetInfo.TypeName.HasChars() && assetInfo.Path.HasChars());
|
|
||||||
|
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
if (!FileSystem::FileExists(assetInfo.Path))
|
if (!FileSystem::FileExists(assetInfo.Path))
|
||||||
{
|
{
|
||||||
LOG(Error, "Cannot find file '{0}'", assetInfo.Path);
|
LOG(Error, "Cannot find file '{0}'", assetInfo.Path);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Find asset factory based in its type
|
// Find asset factory based in its type
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ namespace FlaxEngine
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
var dataTypeName = DataTypeName;
|
var dataTypeName = DataTypeName;
|
||||||
|
if (string.IsNullOrEmpty(dataTypeName))
|
||||||
|
{
|
||||||
|
Debug.LogError(string.Format("Missing typename of data in Json asset '{0}'.", Path), this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
var assemblies = Utils.GetAssemblies();
|
var assemblies = Utils.GetAssemblies();
|
||||||
for (int i = 0; i < assemblies.Length; i++)
|
for (int i = 0; i < assemblies.Length; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -637,9 +637,10 @@ void DebugDrawService::Update()
|
|||||||
desc.VS = shader->GetVS("VS");
|
desc.VS = shader->GetVS("VS");
|
||||||
|
|
||||||
// Default
|
// Default
|
||||||
desc.PS = shader->GetPS("PS");
|
desc.PS = shader->GetPS("PS", 0);
|
||||||
desc.PrimitiveTopologyType = PrimitiveTopologyType::Line;
|
desc.PrimitiveTopologyType = PrimitiveTopologyType::Line;
|
||||||
failed |= DebugDrawPsLinesDefault.Create(desc);
|
failed |= DebugDrawPsLinesDefault.Create(desc);
|
||||||
|
desc.PS = shader->GetPS("PS", 1);
|
||||||
desc.PrimitiveTopologyType = PrimitiveTopologyType::Triangle;
|
desc.PrimitiveTopologyType = PrimitiveTopologyType::Triangle;
|
||||||
failed |= DebugDrawPsTrianglesDefault.Create(desc);
|
failed |= DebugDrawPsTrianglesDefault.Create(desc);
|
||||||
desc.Wireframe = true;
|
desc.Wireframe = true;
|
||||||
@@ -647,9 +648,10 @@ void DebugDrawService::Update()
|
|||||||
|
|
||||||
// Depth Test
|
// Depth Test
|
||||||
desc.Wireframe = false;
|
desc.Wireframe = false;
|
||||||
desc.PS = shader->GetPS("PS_DepthTest");
|
desc.PS = shader->GetPS("PS", 2);
|
||||||
desc.PrimitiveTopologyType = PrimitiveTopologyType::Line;
|
desc.PrimitiveTopologyType = PrimitiveTopologyType::Line;
|
||||||
failed |= DebugDrawPsLinesDepthTest.Create(desc);
|
failed |= DebugDrawPsLinesDepthTest.Create(desc);
|
||||||
|
desc.PS = shader->GetPS("PS", 3);
|
||||||
desc.PrimitiveTopologyType = PrimitiveTopologyType::Triangle;
|
desc.PrimitiveTopologyType = PrimitiveTopologyType::Triangle;
|
||||||
failed |= DebugDrawPsTrianglesDepthTest.Create(desc);
|
failed |= DebugDrawPsTrianglesDepthTest.Create(desc);
|
||||||
desc.Wireframe = true;
|
desc.Wireframe = true;
|
||||||
|
|||||||
@@ -903,12 +903,11 @@ bool LevelImpl::unloadScene(Scene* scene)
|
|||||||
bool LevelImpl::unloadScenes()
|
bool LevelImpl::unloadScenes()
|
||||||
{
|
{
|
||||||
auto scenes = Level::Scenes;
|
auto scenes = Level::Scenes;
|
||||||
for (int32 i = 0; i < scenes.Count(); i++)
|
for (int32 i = scenes.Count() - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (unloadScene(scenes[i]))
|
if (unloadScene(scenes[i]))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1210,6 +1209,15 @@ bool LevelImpl::saveScene(Scene* scene, const String& path)
|
|||||||
|
|
||||||
LOG(Info, "Scene saved! Time {0} ms", Math::CeilToInt((float)(DateTime::NowUTC() - startTime).GetTotalMilliseconds()));
|
LOG(Info, "Scene saved! Time {0} ms", Math::CeilToInt((float)(DateTime::NowUTC() - startTime).GetTotalMilliseconds()));
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
// Reload asset at the target location if is loaded
|
||||||
|
Asset* asset = Content::GetAsset(sceneId);
|
||||||
|
if (!asset)
|
||||||
|
asset = Content::GetAsset(path);
|
||||||
|
if (asset)
|
||||||
|
asset->Reload();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Fire event
|
// Fire event
|
||||||
CallSceneEvent(SceneEventType::OnSceneSaved, scene, sceneId);
|
CallSceneEvent(SceneEventType::OnSceneSaved, scene, sceneId);
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,8 @@ public:
|
|||||||
Dictionary<Guid, int32> PrefabInstanceIdToDataIndex;
|
Dictionary<Guid, int32> PrefabInstanceIdToDataIndex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
typedef Array<PrefabInstanceData> PrefabInstancesData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Collects all the valid prefab instances to update on prefab data synchronization.
|
/// Collects all the valid prefab instances to update on prefab data synchronization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -138,15 +140,15 @@ public:
|
|||||||
/// <param name="prefabId">The prefab asset identifier.</param>
|
/// <param name="prefabId">The prefab asset identifier.</param>
|
||||||
/// <param name="defaultInstance">The default instance (prefab internal, can be null).</param>
|
/// <param name="defaultInstance">The default instance (prefab internal, can be null).</param>
|
||||||
/// <param name="targetActor">The target actor (optional actor to skip for counting, can be null).</param>
|
/// <param name="targetActor">The target actor (optional actor to skip for counting, can be null).</param>
|
||||||
static void CollectPrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor);
|
static void CollectPrefabInstances(PrefabInstancesData& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializes all the prefab instances local changes to restore on prefab data synchronization.
|
/// Serializes all the prefab instances local changes to restore on prefab data synchronization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="prefabInstancesData">The prefab instances data.</param>
|
/// <param name="prefabInstancesData">The prefab instances data.</param>
|
||||||
/// <param name="tmpBuffer">The temporary json buffer (cleared before and after usage).</param>
|
/// <param name="tmpBuffer">The temporary json buffer (cleared before and after usage).</param>
|
||||||
/// <param name="sceneObjects">The scene objects collection cache (cleared before and after usage).</param>
|
/// <param name="prefab">Source prefab.</param>
|
||||||
static void SerializePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, SceneObjectsListCacheType& sceneObjects);
|
static void SerializePrefabInstances(PrefabInstancesData& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, const Prefab* prefab);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronizes the prefab instances by applying changes from the diff data and restoring the local changes captured by SerializePrefabInstances.
|
/// Synchronizes the prefab instances by applying changes from the diff data and restoring the local changes captured by SerializePrefabInstances.
|
||||||
@@ -158,7 +160,7 @@ public:
|
|||||||
/// <param name="prefabObjectIdToDiffData">The hash table that maps the prefab object id to json data for the given prefab object.</param>
|
/// <param name="prefabObjectIdToDiffData">The hash table that maps the prefab object id to json data for the given prefab object.</param>
|
||||||
/// <param name="newPrefabObjectIds">The collection of the new prefab objects ids added to prefab during changes synchronization or modifications apply.</param>
|
/// <param name="newPrefabObjectIds">The collection of the new prefab objects ids added to prefab during changes synchronization or modifications apply.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
static bool SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array<Guid>& newPrefabObjectIds);
|
static bool SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array<Guid>& newPrefabObjectIds);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronizes the prefab instances by applying changes from the diff data and restoring the local changes captured by SerializePrefabInstances.
|
/// Synchronizes the prefab instances by applying changes from the diff data and restoring the local changes captured by SerializePrefabInstances.
|
||||||
@@ -171,10 +173,10 @@ public:
|
|||||||
/// <param name="oldObjectsIds">Collection with ids of the objects (actors and scripts) from the prefab before changes apply. Used to find new objects or old objects and use this information during changes sync (eg. generate ids for the new objects to prevent ids collisions).</param>
|
/// <param name="oldObjectsIds">Collection with ids of the objects (actors and scripts) from the prefab before changes apply. Used to find new objects or old objects and use this information during changes sync (eg. generate ids for the new objects to prevent ids collisions).</param>
|
||||||
/// <param name="newObjectIds">Collection with ids of the objects (actors and scripts) from the prefab after changes apply. Used to find new objects or old objects and use this information during changes sync (eg. generate ids for the new objects to prevent ids collisions).</param>
|
/// <param name="newObjectIds">Collection with ids of the objects (actors and scripts) from the prefab after changes apply. Used to find new objects or old objects and use this information during changes sync (eg. generate ids for the new objects to prevent ids collisions).</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
static bool SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& newObjectIds);
|
static bool SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& newObjectIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
void PrefabInstanceData::CollectPrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor)
|
void PrefabInstanceData::CollectPrefabInstances(PrefabInstancesData& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor)
|
||||||
{
|
{
|
||||||
ScopeLock lock(PrefabManager::PrefabsReferencesLocker);
|
ScopeLock lock(PrefabManager::PrefabsReferencesLocker);
|
||||||
if (PrefabManager::PrefabsReferences.ContainsKey(prefabId))
|
if (PrefabManager::PrefabsReferences.ContainsKey(prefabId))
|
||||||
@@ -207,11 +209,12 @@ void PrefabInstanceData::CollectPrefabInstances(Array<PrefabInstanceData>& prefa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrefabInstanceData::SerializePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, SceneObjectsListCacheType& sceneObjects)
|
void PrefabInstanceData::SerializePrefabInstances(PrefabInstancesData& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, const Prefab* prefab)
|
||||||
{
|
{
|
||||||
if (prefabInstancesData.IsEmpty())
|
if (prefabInstancesData.IsEmpty())
|
||||||
return;
|
return;
|
||||||
|
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||||
|
sceneObjects->EnsureCapacity(prefab->ObjectsCount * 4);
|
||||||
for (int32 dataIndex = 0; dataIndex < prefabInstancesData.Count(); dataIndex++)
|
for (int32 dataIndex = 0; dataIndex < prefabInstancesData.Count(); dataIndex++)
|
||||||
{
|
{
|
||||||
auto& instance = prefabInstancesData[dataIndex];
|
auto& instance = prefabInstancesData[dataIndex];
|
||||||
@@ -253,12 +256,10 @@ void PrefabInstanceData::SerializePrefabInstances(Array<PrefabInstanceData>& pre
|
|||||||
instance.PrefabInstanceIdToDataIndex.Add(obj->GetSceneObjectId(), i);
|
instance.PrefabInstanceIdToDataIndex.Add(obj->GetSceneObjectId(), i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sceneObjects->Clear();
|
|
||||||
tmpBuffer.Clear();
|
tmpBuffer.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array<Guid>& newPrefabObjectIds)
|
bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array<Guid>& newPrefabObjectIds)
|
||||||
{
|
{
|
||||||
for (int32 instanceIndex = 0; instanceIndex < prefabInstancesData.Count(); instanceIndex++)
|
for (int32 instanceIndex = 0; instanceIndex < prefabInstancesData.Count(); instanceIndex++)
|
||||||
{
|
{
|
||||||
@@ -492,7 +493,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& p
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& newObjectIds)
|
bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& newObjectIds)
|
||||||
{
|
{
|
||||||
if (prefabInstancesData.IsEmpty())
|
if (prefabInstancesData.IsEmpty())
|
||||||
return false;
|
return false;
|
||||||
@@ -588,8 +589,6 @@ bool FindCyclicReferences(Actor* actor, const Guid& prefabRootId)
|
|||||||
|
|
||||||
bool Prefab::ApplyAll(Actor* targetActor)
|
bool Prefab::ApplyAll(Actor* targetActor)
|
||||||
{
|
{
|
||||||
// TODO: use more cached dictionaries and other collections containers to prevent memory allocations during apply (optimize for apply 10 times per second the same prefab on many changes in editor)
|
|
||||||
|
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
const auto startTime = DateTime::NowUTC();
|
const auto startTime = DateTime::NowUTC();
|
||||||
|
|
||||||
@@ -695,22 +694,40 @@ bool Prefab::ApplyAll(Actor* targetActor)
|
|||||||
|
|
||||||
ObjectsRemovalService::Flush();
|
ObjectsRemovalService::Flush();
|
||||||
|
|
||||||
|
// Collect existing prefab instances (this and nested ones) to cache 'before' state used later to restore it
|
||||||
|
PrefabInstancesData thisPrefabInstancesData;
|
||||||
|
Array<PrefabInstancesData> allPrefabsInstancesData;
|
||||||
|
{
|
||||||
|
PROFILE_CPU_NAMED("Prefab.CachePrefabInstancesData");
|
||||||
|
|
||||||
|
rapidjson_flax::StringBuffer dataBuffer;
|
||||||
|
PrefabInstanceData::CollectPrefabInstances(thisPrefabInstancesData, GetID(), _defaultInstance, targetActor);
|
||||||
|
PrefabInstanceData::SerializePrefabInstances(thisPrefabInstancesData, dataBuffer, this);
|
||||||
|
|
||||||
|
allPrefabsInstancesData.Resize(allPrefabs.Count());
|
||||||
|
for (int32 i = 0; i < allPrefabs.Count(); i++)
|
||||||
|
{
|
||||||
|
Prefab* prefab = allPrefabs[i];
|
||||||
|
PrefabInstanceData::CollectPrefabInstances(allPrefabsInstancesData[i], prefab->GetID(), prefab->GetDefaultInstance(), prefab->GetDefaultInstance());
|
||||||
|
PrefabInstanceData::SerializePrefabInstances(allPrefabsInstancesData[i], dataBuffer, prefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Use internal call to improve shared collections memory sharing
|
// Use internal call to improve shared collections memory sharing
|
||||||
if (ApplyAllInternal(targetActor, true))
|
if (ApplyAllInternal(targetActor, true, thisPrefabInstancesData))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
SyncNestedPrefabs(allPrefabs);
|
SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData);
|
||||||
|
|
||||||
const auto endTime = DateTime::NowUTC();
|
const auto endTime = DateTime::NowUTC();
|
||||||
LOG(Info, "Prefab updated! {0} ms", (int32)(endTime - startTime).GetTotalMilliseconds());
|
LOG(Info, "Prefab updated! {0} ms", (int32)(endTime - startTime).GetTotalMilliseconds());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab)
|
bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData)
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Prefab.Apply");
|
PROFILE_CPU_NAMED("Prefab.Apply");
|
||||||
ScopeLock lock(Locker);
|
ScopeLock lock(Locker);
|
||||||
|
|
||||||
const auto prefabId = GetID();
|
const auto prefabId = GetID();
|
||||||
|
|
||||||
// Gather all scene objects in target instance (reused later)
|
// Gather all scene objects in target instance (reused later)
|
||||||
@@ -719,9 +736,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
|||||||
SceneQuery::GetAllSerializableSceneObjects(targetActor, *targetObjects.Value);
|
SceneQuery::GetAllSerializableSceneObjects(targetActor, *targetObjects.Value);
|
||||||
if (PrefabManager::FilterPrefabInstancesToSave(prefabId, *targetObjects.Value))
|
if (PrefabManager::FilterPrefabInstancesToSave(prefabId, *targetObjects.Value))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
LOG(Info, "Applying prefab changes from actor {0} (total objects count: {2}) to {1}...", targetActor->ToString(), ToString(), targetObjects->Count());
|
LOG(Info, "Applying prefab changes from actor {0} (total objects count: {2}) to {1}...", targetActor->ToString(), ToString(), targetObjects->Count());
|
||||||
|
|
||||||
Array<Guid> oldObjectsIds = ObjectsIds;
|
Array<Guid> oldObjectsIds = ObjectsIds;
|
||||||
|
|
||||||
// Serialize to json data
|
// Serialize to json data
|
||||||
@@ -809,17 +824,8 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
|||||||
// TODO: what if user applied prefab with references to the other objects from scene? clear them or what?
|
// TODO: what if user applied prefab with references to the other objects from scene? clear them or what?
|
||||||
JsonTools::ChangeIds(diffDataDocument, objectInstanceIdToPrefabObjectId);
|
JsonTools::ChangeIds(diffDataDocument, objectInstanceIdToPrefabObjectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataBuffer.Clear();
|
dataBuffer.Clear();
|
||||||
|
|
||||||
// Fetch all existing prefab instances
|
|
||||||
Array<PrefabInstanceData> prefabInstancesData;
|
|
||||||
PrefabInstanceData::CollectPrefabInstances(prefabInstancesData, prefabId, _defaultInstance, targetActor);
|
|
||||||
|
|
||||||
// Serialize all prefab instances data (temporary storage)
|
|
||||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||||
sceneObjects->EnsureCapacity(ObjectsCount * 4);
|
|
||||||
PrefabInstanceData::SerializePrefabInstances(prefabInstancesData, dataBuffer, sceneObjects);
|
|
||||||
|
|
||||||
// Destroy default instance and some cache data in Prefab
|
// Destroy default instance and some cache data in Prefab
|
||||||
DeleteDefaultInstance();
|
DeleteDefaultInstance();
|
||||||
@@ -1204,18 +1210,7 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Prefab::SyncChanges(const NestedPrefabsList& allPrefabs)
|
bool Prefab::SyncChangesInternal(PrefabInstancesData& prefabInstancesData)
|
||||||
{
|
|
||||||
// Use internal call to improve shared collections memory sharing
|
|
||||||
if (SyncChangesInternal())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
SyncNestedPrefabs(allPrefabs);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Prefab::SyncChangesInternal()
|
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Prefab.SyncChanges");
|
PROFILE_CPU_NAMED("Prefab.SyncChanges");
|
||||||
|
|
||||||
@@ -1252,10 +1247,10 @@ bool Prefab::SyncChangesInternal()
|
|||||||
AutoActorCleanup cleanupDefaultInstance(targetActor);
|
AutoActorCleanup cleanupDefaultInstance(targetActor);
|
||||||
|
|
||||||
// Apply changes
|
// Apply changes
|
||||||
return ApplyAllInternal(targetActor, false);
|
return ApplyAllInternal(targetActor, false, prefabInstancesData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs)
|
void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData) const
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
LOG(Info, "Updating referencing prefabs");
|
LOG(Info, "Updating referencing prefabs");
|
||||||
@@ -1274,9 +1269,13 @@ void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nestedPrefab->NestedPrefabs.Contains(GetID()))
|
const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID());
|
||||||
|
if (nestedPrefabIndex != -1)
|
||||||
{
|
{
|
||||||
nestedPrefab->SyncChanges(allPrefabs);
|
if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[nestedPrefabIndex]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData);
|
||||||
|
|
||||||
ObjectsRemovalService::Flush();
|
ObjectsRemovalService::Flush();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,12 +82,12 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
|
typedef Array<class PrefabInstanceData> PrefabInstancesData;
|
||||||
typedef Array<AssetReference<Prefab>> NestedPrefabsList;
|
typedef Array<AssetReference<Prefab>> NestedPrefabsList;
|
||||||
bool ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab);
|
bool ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData);
|
||||||
bool UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, rapidjson_flax::StringBuffer& tmpBuffer);
|
bool UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, rapidjson_flax::StringBuffer& tmpBuffer);
|
||||||
bool SyncChanges(const NestedPrefabsList& allPrefabs);
|
bool SyncChangesInternal(PrefabInstancesData& prefabInstancesData);
|
||||||
bool SyncChangesInternal();
|
void SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData) const;
|
||||||
void SyncNestedPrefabs(const NestedPrefabsList& allPrefabs);
|
|
||||||
#endif
|
#endif
|
||||||
void DeleteDefaultInstance();
|
void DeleteDefaultInstance();
|
||||||
|
|
||||||
|
|||||||
@@ -7,17 +7,14 @@
|
|||||||
Actor* SceneQuery::RaycastScene(const Ray& ray)
|
Actor* SceneQuery::RaycastScene(const Ray& ray)
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
|
|
||||||
#if SCENE_QUERIES_WITH_LOCK
|
#if SCENE_QUERIES_WITH_LOCK
|
||||||
ScopeLock lock(Level::ScenesLock);
|
ScopeLock lock(Level::ScenesLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Actor* target;
|
Actor* target;
|
||||||
Actor* minTarget = nullptr;
|
Actor* minTarget = nullptr;
|
||||||
Real distance;
|
Real distance;
|
||||||
Vector3 normal;
|
Vector3 normal;
|
||||||
Real minDistance = MAX_Real;
|
Real minDistance = MAX_Real;
|
||||||
|
|
||||||
for (int32 i = 0; i < Level::Scenes.Count(); i++)
|
for (int32 i = 0; i < Level::Scenes.Count(); i++)
|
||||||
{
|
{
|
||||||
target = Level::Scenes[i]->Intersects(ray, distance, normal);
|
target = Level::Scenes[i]->Intersects(ray, distance, normal);
|
||||||
@@ -30,7 +27,6 @@ Actor* SceneQuery::RaycastScene(const Ray& ray)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return minTarget;
|
return minTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,16 +34,13 @@ bool GetAllSceneObjectsQuery(Actor* actor, Array<SceneObject*>& objects)
|
|||||||
{
|
{
|
||||||
objects.Add(actor);
|
objects.Add(actor);
|
||||||
objects.Add(reinterpret_cast<SceneObject* const*>(actor->Scripts.Get()), actor->Scripts.Count());
|
objects.Add(reinterpret_cast<SceneObject* const*>(actor->Scripts.Get()), actor->Scripts.Count());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneQuery::GetAllSceneObjects(Actor* root, Array<SceneObject*>& objects)
|
void SceneQuery::GetAllSceneObjects(Actor* root, Array<SceneObject*>& objects)
|
||||||
{
|
{
|
||||||
ASSERT(root);
|
ASSERT(root);
|
||||||
|
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
|
|
||||||
Function<bool(Actor*, Array<SceneObject*>&)> func(GetAllSceneObjectsQuery);
|
Function<bool(Actor*, Array<SceneObject*>&)> func(GetAllSceneObjectsQuery);
|
||||||
root->TreeExecuteChildren<Array<SceneObject*>&>(func, objects);
|
root->TreeExecuteChildren<Array<SceneObject*>&>(func, objects);
|
||||||
}
|
}
|
||||||
@@ -56,19 +49,15 @@ bool GetAllSerializableSceneObjectsQuery(Actor* actor, Array<SceneObject*>& obje
|
|||||||
{
|
{
|
||||||
if (EnumHasAnyFlags(actor->HideFlags, HideFlags::DontSave))
|
if (EnumHasAnyFlags(actor->HideFlags, HideFlags::DontSave))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
objects.Add(actor);
|
objects.Add(actor);
|
||||||
objects.Add(reinterpret_cast<SceneObject* const*>(actor->Scripts.Get()), actor->Scripts.Count());
|
objects.Add(reinterpret_cast<SceneObject* const*>(actor->Scripts.Get()), actor->Scripts.Count());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneQuery::GetAllSerializableSceneObjects(Actor* root, Array<SceneObject*>& objects)
|
void SceneQuery::GetAllSerializableSceneObjects(Actor* root, Array<SceneObject*>& objects)
|
||||||
{
|
{
|
||||||
ASSERT(root);
|
ASSERT(root);
|
||||||
|
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
|
|
||||||
Function<bool(Actor*, Array<SceneObject*>&)> func(GetAllSerializableSceneObjectsQuery);
|
Function<bool(Actor*, Array<SceneObject*>&)> func(GetAllSerializableSceneObjectsQuery);
|
||||||
root->TreeExecute<Array<SceneObject*>&>(func, objects);
|
root->TreeExecute<Array<SceneObject*>&>(func, objects);
|
||||||
}
|
}
|
||||||
@@ -82,9 +71,7 @@ bool GetAllActorsQuery(Actor* actor, Array<Actor*>& actors)
|
|||||||
void SceneQuery::GetAllActors(Actor* root, Array<Actor*>& actors)
|
void SceneQuery::GetAllActors(Actor* root, Array<Actor*>& actors)
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
|
|
||||||
ASSERT(root);
|
ASSERT(root);
|
||||||
|
|
||||||
Function<bool(Actor*, Array<Actor*>&)> func(GetAllActorsQuery);
|
Function<bool(Actor*, Array<Actor*>&)> func(GetAllActorsQuery);
|
||||||
root->TreeExecuteChildren<Array<Actor*>&>(func, actors);
|
root->TreeExecuteChildren<Array<Actor*>&>(func, actors);
|
||||||
}
|
}
|
||||||
@@ -92,11 +79,9 @@ void SceneQuery::GetAllActors(Actor* root, Array<Actor*>& actors)
|
|||||||
void SceneQuery::GetAllActors(Array<Actor*>& actors)
|
void SceneQuery::GetAllActors(Array<Actor*>& actors)
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
|
|
||||||
#if SCENE_QUERIES_WITH_LOCK
|
#if SCENE_QUERIES_WITH_LOCK
|
||||||
ScopeLock lock(Level::ScenesLock);
|
ScopeLock lock(Level::ScenesLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (int32 i = 0; i < Level::Scenes.Count(); i++)
|
for (int32 i = 0; i < Level::Scenes.Count(); i++)
|
||||||
GetAllActors(Level::Scenes[i], actors);
|
GetAllActors(Level::Scenes[i], actors);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,21 @@ Tag Tags::Get(const StringView& tagName)
|
|||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Tags::HasTag(const Array<Tag>& list, const Tag& tag)
|
Array<Tag> Tags::GetSubTags(Tag parentTag)
|
||||||
|
{
|
||||||
|
Array<Tag> subTags;
|
||||||
|
const String& parentTagName = parentTag.ToString();
|
||||||
|
for (int32 i = 0; i < List.Count(); i++)
|
||||||
|
{
|
||||||
|
const Tag tag(i + 1);
|
||||||
|
const String& tagName = List[i];
|
||||||
|
if (tagName.StartsWith(parentTagName) && parentTag.Index != tag.Index)
|
||||||
|
subTags.Add(tag);
|
||||||
|
}
|
||||||
|
return subTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tags::HasTag(const Array<Tag>& list, const Tag tag)
|
||||||
{
|
{
|
||||||
if (tag.Index == 0)
|
if (tag.Index == 0)
|
||||||
return false;
|
return false;
|
||||||
@@ -69,7 +83,7 @@ bool Tags::HasTag(const Array<Tag>& list, const Tag& tag)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Tags::HasTagExact(const Array<Tag>& list, const Tag& tag)
|
bool Tags::HasTagExact(const Array<Tag>& list, const Tag tag)
|
||||||
{
|
{
|
||||||
if (tag.Index == 0)
|
if (tag.Index == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -92,6 +92,13 @@ API_CLASS(Static) class FLAXENGINE_API Tags
|
|||||||
/// <returns>The tag.</returns>
|
/// <returns>The tag.</returns>
|
||||||
API_FUNCTION() static Tag Get(const StringView& tagName);
|
API_FUNCTION() static Tag Get(const StringView& tagName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all subtags of the specific Tag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
API_FUNCTION() static Array<Tag> GetSubTags(Tag tag);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the list of tags contains a given tag (including parent tags check). For example, HasTag({"A.B"}, "A") returns true, for exact check use HasTagExact.
|
/// Checks if the list of tags contains a given tag (including parent tags check). For example, HasTag({"A.B"}, "A") returns true, for exact check use HasTagExact.
|
||||||
@@ -99,7 +106,7 @@ public:
|
|||||||
/// <param name="list">The tags list to use.</param>
|
/// <param name="list">The tags list to use.</param>
|
||||||
/// <param name="tag">The tag to check.</param>
|
/// <param name="tag">The tag to check.</param>
|
||||||
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
||||||
static bool HasTag(const Array<Tag>& list, const Tag& tag);
|
static bool HasTag(const Array<Tag>& list, const Tag tag);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag.
|
/// Checks if the list of tags contains a given tag (exact match). For example, HasTag({"A.B"}, "A") returns false, for parents check use HasTag.
|
||||||
@@ -107,7 +114,7 @@ public:
|
|||||||
/// <param name="list">The tags list to use.</param>
|
/// <param name="list">The tags list to use.</param>
|
||||||
/// <param name="tag">The tag to check.</param>
|
/// <param name="tag">The tag to check.</param>
|
||||||
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
||||||
static bool HasTagExact(const Array<Tag>& list, const Tag& tag);
|
static bool HasTagExact(const Array<Tag>& list, const Tag tag);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact.
|
/// Checks if the list of tags contains any of the given tags (including parent tags check). For example, HasAny({"A.B", "C"}, {"A"}) returns true, for exact check use HasAnyExact.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ REGISTER_JSON_ASSET(LocalizedStringTable, "FlaxEngine.LocalizedStringTable", tru
|
|||||||
LocalizedStringTable::LocalizedStringTable(const SpawnParams& params, const AssetInfo* info)
|
LocalizedStringTable::LocalizedStringTable(const SpawnParams& params, const AssetInfo* info)
|
||||||
: JsonAssetBase(params, info)
|
: JsonAssetBase(params, info)
|
||||||
{
|
{
|
||||||
|
DataTypeName = TypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalizedStringTable::AddString(const StringView& id, const StringView& value)
|
void LocalizedStringTable::AddString(const StringView& id, const StringView& value)
|
||||||
|
|||||||
@@ -861,7 +861,7 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj)
|
|||||||
DeleteNetworkObject(obj);
|
DeleteNetworkObject(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 NetworkReplicator::GetObjectOwnerClientId(ScriptingObject* obj)
|
uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
uint32 id = NetworkManager::ServerClientId;
|
uint32 id = NetworkManager::ServerClientId;
|
||||||
if (obj)
|
if (obj)
|
||||||
@@ -870,11 +870,23 @@ uint32 NetworkReplicator::GetObjectOwnerClientId(ScriptingObject* obj)
|
|||||||
const auto it = Objects.Find(obj->GetID());
|
const auto it = Objects.Find(obj->GetID());
|
||||||
if (it != Objects.End())
|
if (it != Objects.End())
|
||||||
id = it->Item.OwnerClientId;
|
id = it->Item.OwnerClientId;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (const SpawnItem& item : SpawnQueue)
|
||||||
|
{
|
||||||
|
if (item.Object == obj)
|
||||||
|
{
|
||||||
|
if (item.HasOwnership)
|
||||||
|
id = item.OwnerClientId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkObjectRole NetworkReplicator::GetObjectRole(ScriptingObject* obj)
|
NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
NetworkObjectRole role = NetworkObjectRole::None;
|
NetworkObjectRole role = NetworkObjectRole::None;
|
||||||
if (obj)
|
if (obj)
|
||||||
@@ -883,6 +895,18 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(ScriptingObject* obj)
|
|||||||
const auto it = Objects.Find(obj->GetID());
|
const auto it = Objects.Find(obj->GetID());
|
||||||
if (it != Objects.End())
|
if (it != Objects.End())
|
||||||
role = it->Item.Role;
|
role = it->Item.Role;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (const SpawnItem& item : SpawnQueue)
|
||||||
|
{
|
||||||
|
if (item.Object == obj)
|
||||||
|
{
|
||||||
|
if (item.HasOwnership)
|
||||||
|
role = item.Role;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
@@ -901,6 +925,18 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
|
|||||||
auto& item = SpawnQueue[i];
|
auto& item = SpawnQueue[i];
|
||||||
if (item.Object == obj)
|
if (item.Object == obj)
|
||||||
{
|
{
|
||||||
|
#if !BUILD_RELEASE
|
||||||
|
if (ownerClientId == NetworkManager::LocalClientId)
|
||||||
|
{
|
||||||
|
// Ensure local client owns that object actually
|
||||||
|
CHECK(localRole == NetworkObjectRole::OwnedAuthoritative);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ensure local client doesn't own that object since it's owned by other client
|
||||||
|
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
item.HasOwnership = true;
|
item.HasOwnership = true;
|
||||||
item.HierarchicalOwnership = hierarchical;
|
item.HierarchicalOwnership = hierarchical;
|
||||||
item.OwnerClientId = ownerClientId;
|
item.OwnerClientId = ownerClientId;
|
||||||
@@ -1469,8 +1505,8 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recreate object locally (spawn only root)
|
// Recreate object locally (spawn only root)
|
||||||
ScriptingObject* obj = nullptr;
|
|
||||||
Actor* prefabInstance = nullptr;
|
Actor* prefabInstance = nullptr;
|
||||||
|
Array<ScriptingObject*> objects;
|
||||||
if (msgData.PrefabId.IsValid())
|
if (msgData.PrefabId.IsValid())
|
||||||
{
|
{
|
||||||
const NetworkReplicatedObject* parent = ResolveObject(rootItem.ParentId);
|
const NetworkReplicatedObject* parent = ResolveObject(rootItem.ParentId);
|
||||||
@@ -1489,7 +1525,7 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl
|
|||||||
{
|
{
|
||||||
if (Objects.Contains(child->GetID()))
|
if (Objects.Contains(child->GetID()))
|
||||||
{
|
{
|
||||||
obj = FindPrefabObject(child, rootItem.PrefabObjectID);
|
ScriptingObject* obj = FindPrefabObject(child, rootItem.PrefabObjectID);
|
||||||
if (Objects.Contains(obj->GetID()))
|
if (Objects.Contains(obj->GetID()))
|
||||||
{
|
{
|
||||||
// Other instance with already spawned network object
|
// Other instance with already spawned network object
|
||||||
@@ -1521,46 +1557,77 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!obj)
|
|
||||||
obj = FindPrefabObject(prefabInstance, rootItem.PrefabObjectID);
|
// Resolve objects from prefab instance
|
||||||
if (!obj)
|
objects.Resize(msgData.ItemsCount);
|
||||||
|
for (int32 i = 0; i < msgData.ItemsCount; i++)
|
||||||
{
|
{
|
||||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} in prefab {}", rootItem.PrefabObjectID.ToString(), msgData.PrefabId.ToString());
|
auto& msgDataItem = msgDataItems[i];
|
||||||
Delete(prefabInstance);
|
ScriptingObject* obj = FindPrefabObject(prefabInstance, msgDataItem.PrefabObjectID);
|
||||||
return;
|
if (!obj)
|
||||||
|
{
|
||||||
|
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} in prefab {}", msgDataItem.PrefabObjectID.ToString(), msgData.PrefabId.ToString());
|
||||||
|
Delete(prefabInstance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
objects[i] = obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (msgData.ItemsCount == 1)
|
||||||
{
|
{
|
||||||
// Spawn object
|
// Spawn object
|
||||||
if (msgData.ItemsCount != 1)
|
|
||||||
{
|
|
||||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Only prefab object spawning can contain more than one object (for type {})", String(rootItem.ObjectTypeName));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ScriptingTypeHandle objectType = Scripting::FindScriptingType(rootItem.ObjectTypeName);
|
const ScriptingTypeHandle objectType = Scripting::FindScriptingType(rootItem.ObjectTypeName);
|
||||||
obj = ScriptingObject::NewObject(objectType);
|
ScriptingObject* obj = ScriptingObject::NewObject(objectType);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
{
|
{
|
||||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to spawn object type {}", String(rootItem.ObjectTypeName));
|
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to spawn object type {}", String(rootItem.ObjectTypeName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
objects.Add(obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Spawn objects
|
||||||
|
objects.Resize(msgData.ItemsCount);
|
||||||
|
for (int32 i = 0; i < msgData.ItemsCount; i++)
|
||||||
|
{
|
||||||
|
auto& msgDataItem = msgDataItems[i];
|
||||||
|
const ScriptingTypeHandle objectType = Scripting::FindScriptingType(msgDataItem.ObjectTypeName);
|
||||||
|
ScriptingObject* obj = ScriptingObject::NewObject(objectType);
|
||||||
|
if (!obj)
|
||||||
|
{
|
||||||
|
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to spawn object type {}", String(msgDataItem.ObjectTypeName));
|
||||||
|
for (ScriptingObject* e : objects)
|
||||||
|
Delete(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
objects[i] = obj;
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
|
// Link hierarchy of spawned objects before calling any networking code for them
|
||||||
|
if (auto sceneObject = ScriptingObject::Cast<SceneObject>(obj))
|
||||||
|
{
|
||||||
|
Actor* parent = nullptr;
|
||||||
|
for (int32 j = 0; j < i; j++)
|
||||||
|
{
|
||||||
|
if (msgDataItems[j].ObjectId == msgDataItem.ParentId)
|
||||||
|
{
|
||||||
|
parent = ScriptingObject::Cast<Actor>(objects[j]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parent)
|
||||||
|
sceneObject->SetParent(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup all newly spawned objects
|
// Setup all newly spawned objects
|
||||||
for (int32 i = 0; i < msgData.ItemsCount; i++)
|
for (int32 i = 0; i < msgData.ItemsCount; i++)
|
||||||
{
|
{
|
||||||
auto& msgDataItem = msgDataItems[i];
|
auto& msgDataItem = msgDataItems[i];
|
||||||
if (i != 0)
|
ScriptingObject* obj = objects[i];
|
||||||
{
|
|
||||||
obj = FindPrefabObject(prefabInstance, msgDataItem.PrefabObjectID);
|
|
||||||
if (!obj)
|
|
||||||
{
|
|
||||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} in prefab {}", msgDataItem.PrefabObjectID.ToString(), msgData.PrefabId.ToString());
|
|
||||||
Delete(prefabInstance);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!obj->IsRegistered())
|
if (!obj->IsRegistered())
|
||||||
obj->RegisterObject();
|
obj->RegisterObject();
|
||||||
const NetworkReplicatedObject* parent = ResolveObject(msgDataItem.ParentId);
|
const NetworkReplicatedObject* parent = ResolveObject(msgDataItem.ParentId);
|
||||||
|
|||||||
@@ -104,14 +104,14 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The network object.</param>
|
/// <param name="obj">The network object.</param>
|
||||||
/// <returns>The Client Id.</returns>
|
/// <returns>The Client Id.</returns>
|
||||||
API_FUNCTION() static uint32 GetObjectOwnerClientId(ScriptingObject* obj);
|
API_FUNCTION() static uint32 GetObjectOwnerClientId(const ScriptingObject* obj);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the role of the network object used locally (eg. to check if can simulate object).
|
/// Gets the role of the network object used locally (eg. to check if can simulate object).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The network object.</param>
|
/// <param name="obj">The network object.</param>
|
||||||
/// <returns>The object role.</returns>
|
/// <returns>The object role.</returns>
|
||||||
API_FUNCTION() static NetworkObjectRole GetObjectRole(ScriptingObject* obj);
|
API_FUNCTION() static NetworkObjectRole GetObjectRole(const ScriptingObject* obj);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the network object is owned locally (thus current client has authority to manage it).
|
/// Checks if the network object is owned locally (thus current client has authority to manage it).
|
||||||
@@ -119,7 +119,7 @@ public:
|
|||||||
/// <remarks>Equivalent to GetObjectRole == OwnedAuthoritative.</remarks>
|
/// <remarks>Equivalent to GetObjectRole == OwnedAuthoritative.</remarks>
|
||||||
/// <param name="obj">The network object.</param>
|
/// <param name="obj">The network object.</param>
|
||||||
/// <returns>True if object is owned by this client, otherwise false.</returns>
|
/// <returns>True if object is owned by this client, otherwise false.</returns>
|
||||||
API_FUNCTION() FORCE_INLINE static bool IsObjectOwned(ScriptingObject* obj)
|
API_FUNCTION() FORCE_INLINE static bool IsObjectOwned(const ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
return GetObjectRole(obj) == NetworkObjectRole::OwnedAuthoritative;
|
return GetObjectRole(obj) == NetworkObjectRole::OwnedAuthoritative;
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ public:
|
|||||||
/// <remarks>Equivalent to GetObjectRole != Replicated.</remarks>
|
/// <remarks>Equivalent to GetObjectRole != Replicated.</remarks>
|
||||||
/// <param name="obj">The network object.</param>
|
/// <param name="obj">The network object.</param>
|
||||||
/// <returns>True if object is simulated on this client, otherwise false.</returns>
|
/// <returns>True if object is simulated on this client, otherwise false.</returns>
|
||||||
API_FUNCTION() FORCE_INLINE static bool IsObjectSimulated(ScriptingObject* obj)
|
API_FUNCTION() FORCE_INLINE static bool IsObjectSimulated(const ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
return GetObjectRole(obj) != NetworkObjectRole::Replicated;
|
return GetObjectRole(obj) != NetworkObjectRole::Replicated;
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ public:
|
|||||||
/// <remarks>Equivalent to (GetObjectRole == Replicated or GetObjectRole == ReplicatedAutonomous).</remarks>
|
/// <remarks>Equivalent to (GetObjectRole == Replicated or GetObjectRole == ReplicatedAutonomous).</remarks>
|
||||||
/// <param name="obj">The network object.</param>
|
/// <param name="obj">The network object.</param>
|
||||||
/// <returns>True if object is simulated on this client, otherwise false.</returns>
|
/// <returns>True if object is simulated on this client, otherwise false.</returns>
|
||||||
API_FUNCTION() FORCE_INLINE static bool IsObjectReplicated(ScriptingObject* obj)
|
API_FUNCTION() FORCE_INLINE static bool IsObjectReplicated(const ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
const NetworkObjectRole role = GetObjectRole(obj);
|
const NetworkObjectRole role = GetObjectRole(obj);
|
||||||
return role == NetworkObjectRole::Replicated || role == NetworkObjectRole::ReplicatedSimulated;
|
return role == NetworkObjectRole::Replicated || role == NetworkObjectRole::ReplicatedSimulated;
|
||||||
|
|||||||
@@ -388,33 +388,33 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va
|
|||||||
// Random Float Range
|
// Random Float Range
|
||||||
case 213:
|
case 213:
|
||||||
{
|
{
|
||||||
auto& a = node->Values[0].AsFloat;
|
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat();
|
||||||
auto& b = node->Values[1].AsFloat;
|
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat();
|
||||||
value = writeLocal(VariantType::Float, String::Format(TEXT("lerp({0}, {1}, RAND)"), a, b), node);
|
value = writeLocal(VariantType::Float, String::Format(TEXT("lerp({0}, {1}, RAND)"), a.Value, b.Value), node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Random Vector2 Range
|
// Random Vector2 Range
|
||||||
case 214:
|
case 214:
|
||||||
{
|
{
|
||||||
auto& a = node->Values[0].AsFloat2();
|
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat2();
|
||||||
auto& b = node->Values[1].AsFloat2();
|
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat2();
|
||||||
value = writeLocal(VariantType::Float2, String::Format(TEXT("float2(lerp({0}, {1}, RAND), lerp({2}, {3}, RAND))"), a.X, b.X, a.Y, b.Y), node);
|
value = writeLocal(VariantType::Float2, String::Format(TEXT("float2(lerp({0}.x, {1}.x, RAND), lerp({0}.y, {1}.y, RAND))"), a.Value, b.Value), node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Random Vector3 Range
|
// Random Vector3 Range
|
||||||
case 215:
|
case 215:
|
||||||
{
|
{
|
||||||
auto& a = node->Values[0].AsFloat3();
|
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat3();
|
||||||
auto& b = node->Values[1].AsFloat3();
|
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat3();
|
||||||
value = writeLocal(VariantType::Float3, String::Format(TEXT("float3(lerp({0}, {1}, RAND), lerp({2}, {3}, RAND), lerp({4}, {5}, RAND))"), a.X, b.X, a.Y, b.Y, a.Z, b.Z), node);
|
value = writeLocal(VariantType::Float3, String::Format(TEXT("float3(lerp({0}.x, {1}.x, RAND), lerp({0}.y, {1}.y, RAND), lerp({0}.z, {1}.z, RAND))"), a.Value, b.Value), node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Random Vector4 Range
|
// Random Vector4 Range
|
||||||
case 216:
|
case 216:
|
||||||
{
|
{
|
||||||
auto& a = node->Values[0].AsFloat4();
|
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat4();
|
||||||
auto& b = node->Values[1].AsFloat4();
|
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat4();
|
||||||
value = writeLocal(VariantType::Float4, String::Format(TEXT("float4(lerp({0}, {1}, RAND), lerp({2}, {3}, RAND), lerp({4}, {5}, RAND), lerp({6}, {7}, RAND))"), a.X, b.X, a.Y, b.Y, a.Z, b.Z, a.W, b.W), node);
|
value = writeLocal(VariantType::Float4, String::Format(TEXT("float4(lerp({0}.x, {1}.x, RAND), lerp({0}.y, {1}.y, RAND), lerp({0}.z, {1}.z, RAND), lerp({0}.w, {1}.w, RAND))"), a.Value, b.Value), node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Particle Emitter Function
|
// Particle Emitter Function
|
||||||
|
|||||||
@@ -212,6 +212,12 @@ Asset::LoadResult ParticleSystem::load()
|
|||||||
|
|
||||||
int32 version;
|
int32 version;
|
||||||
stream.ReadInt32(&version);
|
stream.ReadInt32(&version);
|
||||||
|
#if USE_EDITOR
|
||||||
|
// Skip unused parameters
|
||||||
|
#define SKIP_UNUSED_PARAM_OVERRIDE() if (key.First < 0 || key.First >= Emitters.Count() || Emitters[key.First] == nullptr || Emitters[key.First]->Graph.GetParameter(key.Second) == nullptr) continue
|
||||||
|
#else
|
||||||
|
#define SKIP_UNUSED_PARAM_OVERRIDE()
|
||||||
|
#endif
|
||||||
switch (version)
|
switch (version)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
@@ -281,13 +287,7 @@ Asset::LoadResult ParticleSystem::load()
|
|||||||
stream.ReadInt32(&key.First);
|
stream.ReadInt32(&key.First);
|
||||||
stream.Read(key.Second);
|
stream.Read(key.Second);
|
||||||
stream.ReadCommonValue(&value);
|
stream.ReadCommonValue(&value);
|
||||||
|
SKIP_UNUSED_PARAM_OVERRIDE();
|
||||||
#if USE_EDITOR
|
|
||||||
// Skip unused parameters
|
|
||||||
if (key.First < 0 || key.First >= Emitters.Count() || Emitters[key.First]->Graph.GetParameter(key.Second) == nullptr)
|
|
||||||
continue;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EmittersParametersOverrides.Add(key, Variant(value));
|
EmittersParametersOverrides.Add(key, Variant(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,13 +361,7 @@ Asset::LoadResult ParticleSystem::load()
|
|||||||
stream.ReadInt32(&key.First);
|
stream.ReadInt32(&key.First);
|
||||||
stream.Read(key.Second);
|
stream.Read(key.Second);
|
||||||
stream.ReadCommonValue(&value);
|
stream.ReadCommonValue(&value);
|
||||||
|
SKIP_UNUSED_PARAM_OVERRIDE();
|
||||||
#if USE_EDITOR
|
|
||||||
// Skip unused parameters
|
|
||||||
if (key.First < 0 || key.First >= Emitters.Count() || Emitters[key.First]->Graph.GetParameter(key.Second) == nullptr)
|
|
||||||
continue;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EmittersParametersOverrides[key] = Variant(value);
|
EmittersParametersOverrides[key] = Variant(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,13 +434,7 @@ Asset::LoadResult ParticleSystem::load()
|
|||||||
stream.ReadInt32(&key.First);
|
stream.ReadInt32(&key.First);
|
||||||
stream.Read(key.Second);
|
stream.Read(key.Second);
|
||||||
stream.ReadVariant(&value);
|
stream.ReadVariant(&value);
|
||||||
|
SKIP_UNUSED_PARAM_OVERRIDE();
|
||||||
#if USE_EDITOR
|
|
||||||
// Skip unused parameters
|
|
||||||
if (key.First < 0 || key.First >= Emitters.Count() || Emitters[key.First]->Graph.GetParameter(key.Second) == nullptr)
|
|
||||||
continue;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EmittersParametersOverrides[key] = value;
|
EmittersParametersOverrides[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,6 +445,7 @@ Asset::LoadResult ParticleSystem::load()
|
|||||||
LOG(Warning, "Unknown timeline version {0}.", version);
|
LOG(Warning, "Unknown timeline version {0}.", version);
|
||||||
return LoadResult::InvalidData;
|
return LoadResult::InvalidData;
|
||||||
}
|
}
|
||||||
|
#undef SKIP_UNUSED_PARAM_OVERRIDE
|
||||||
|
|
||||||
#if !BUILD_RELEASE
|
#if !BUILD_RELEASE
|
||||||
_debugName = StringUtils::GetFileNameWithoutExtension(GetPath());
|
_debugName = StringUtils::GetFileNameWithoutExtension(GetPath());
|
||||||
|
|||||||
@@ -251,3 +251,5 @@ API_CLASS(InBuild) class FLAXENGINE_API PersistentScriptingObject : public Scrip
|
|||||||
public:
|
public:
|
||||||
PersistentScriptingObject(const SpawnParams& params);
|
PersistentScriptingObject(const SpawnParams& params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern FLAXENGINE_API class ScriptingObject* FindObject(const Guid& id, class MClass* type);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace FlaxEngine.Json
|
|||||||
{
|
{
|
||||||
// Skip serialization as reference id for the root object serialization (eg. Script)
|
// Skip serialization as reference id for the root object serialization (eg. Script)
|
||||||
var cache = JsonSerializer.Current.Value;
|
var cache = JsonSerializer.Current.Value;
|
||||||
if (cache != null && cache.IsDuringSerialization && cache.SerializerWriter.SerializeStackSize == 0)
|
if (cache != null && cache.IsWriting && cache.SerializerWriter.SerializeStackSize == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,14 @@ namespace FlaxEngine.Json
|
|||||||
public JsonSerializerInternalWriter SerializerWriter;
|
public JsonSerializerInternalWriter SerializerWriter;
|
||||||
public UnmanagedMemoryStream MemoryStream;
|
public UnmanagedMemoryStream MemoryStream;
|
||||||
public StreamReader Reader;
|
public StreamReader Reader;
|
||||||
public bool IsDuringSerialization;
|
public bool IsWriting;
|
||||||
|
public bool IsReading;
|
||||||
|
|
||||||
public unsafe SerializerCache(JsonSerializerSettings settings)
|
public unsafe SerializerCache(JsonSerializerSettings settings)
|
||||||
{
|
{
|
||||||
JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings);
|
JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings);
|
||||||
JsonSerializer.Formatting = Formatting.Indented;
|
JsonSerializer.Formatting = Formatting.Indented;
|
||||||
|
JsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
|
||||||
StringBuilder = new StringBuilder(256);
|
StringBuilder = new StringBuilder(256);
|
||||||
StringWriter = new StringWriter(StringBuilder, CultureInfo.InvariantCulture);
|
StringWriter = new StringWriter(StringBuilder, CultureInfo.InvariantCulture);
|
||||||
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
|
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
|
||||||
@@ -50,6 +52,49 @@ namespace FlaxEngine.Json
|
|||||||
DateFormatString = JsonSerializer.DateFormatString,
|
DateFormatString = JsonSerializer.DateFormatString,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReadBegin()
|
||||||
|
{
|
||||||
|
if (IsReading)
|
||||||
|
{
|
||||||
|
// TODO: Reset reading state (eg if previous deserialization got exception)
|
||||||
|
}
|
||||||
|
IsWriting = false;
|
||||||
|
IsReading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadEnd()
|
||||||
|
{
|
||||||
|
IsReading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteBegin()
|
||||||
|
{
|
||||||
|
if (IsWriting)
|
||||||
|
{
|
||||||
|
// Reset writing state (eg if previous serialization got exception)
|
||||||
|
JsonWriter = new JsonTextWriter(StringWriter)
|
||||||
|
{
|
||||||
|
IndentChar = '\t',
|
||||||
|
Indentation = 1,
|
||||||
|
Formatting = JsonSerializer.Formatting,
|
||||||
|
DateFormatHandling = JsonSerializer.DateFormatHandling,
|
||||||
|
DateTimeZoneHandling = JsonSerializer.DateTimeZoneHandling,
|
||||||
|
FloatFormatHandling = JsonSerializer.FloatFormatHandling,
|
||||||
|
StringEscapeHandling = JsonSerializer.StringEscapeHandling,
|
||||||
|
Culture = JsonSerializer.Culture,
|
||||||
|
DateFormatString = JsonSerializer.DateFormatString,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
StringBuilder.Clear();
|
||||||
|
IsWriting = true;
|
||||||
|
IsReading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteEnd()
|
||||||
|
{
|
||||||
|
IsWriting = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static JsonSerializerSettings Settings = CreateDefaultSettings(false);
|
internal static JsonSerializerSettings Settings = CreateDefaultSettings(false);
|
||||||
@@ -117,9 +162,9 @@ namespace FlaxEngine.Json
|
|||||||
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
|
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
|
||||||
Current.Value = cache;
|
Current.Value = cache;
|
||||||
|
|
||||||
cache.StringBuilder.Clear();
|
cache.WriteBegin();
|
||||||
cache.IsDuringSerialization = true;
|
|
||||||
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
|
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
|
||||||
|
cache.WriteEnd();
|
||||||
|
|
||||||
return cache.StringBuilder.ToString();
|
return cache.StringBuilder.ToString();
|
||||||
}
|
}
|
||||||
@@ -136,9 +181,9 @@ namespace FlaxEngine.Json
|
|||||||
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
|
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
|
||||||
Current.Value = cache;
|
Current.Value = cache;
|
||||||
|
|
||||||
cache.StringBuilder.Clear();
|
cache.WriteBegin();
|
||||||
cache.IsDuringSerialization = true;
|
|
||||||
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
|
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
|
||||||
|
cache.WriteEnd();
|
||||||
|
|
||||||
return cache.StringBuilder.ToString();
|
return cache.StringBuilder.ToString();
|
||||||
}
|
}
|
||||||
@@ -156,9 +201,9 @@ namespace FlaxEngine.Json
|
|||||||
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
|
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
|
||||||
Current.Value = cache;
|
Current.Value = cache;
|
||||||
|
|
||||||
cache.StringBuilder.Clear();
|
cache.WriteBegin();
|
||||||
cache.IsDuringSerialization = true;
|
|
||||||
cache.SerializerWriter.SerializeDiff(cache.JsonWriter, obj, type, other);
|
cache.SerializerWriter.SerializeDiff(cache.JsonWriter, obj, type, other);
|
||||||
|
cache.WriteEnd();
|
||||||
|
|
||||||
return cache.StringBuilder.ToString();
|
return cache.StringBuilder.ToString();
|
||||||
}
|
}
|
||||||
@@ -171,21 +216,20 @@ namespace FlaxEngine.Json
|
|||||||
public static void Deserialize(object input, string json)
|
public static void Deserialize(object input, string json)
|
||||||
{
|
{
|
||||||
var cache = Cache.Value;
|
var cache = Cache.Value;
|
||||||
cache.IsDuringSerialization = false;
|
cache.ReadBegin();
|
||||||
Current.Value = cache;
|
|
||||||
|
|
||||||
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
|
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
|
||||||
{
|
{
|
||||||
cache.JsonSerializer.Populate(reader, input);
|
cache.JsonSerializer.Populate(reader, input);
|
||||||
|
if (cache.JsonSerializer.CheckAdditionalContent)
|
||||||
if (!cache.JsonSerializer.CheckAdditionalContent)
|
|
||||||
return;
|
|
||||||
while (reader.Read())
|
|
||||||
{
|
{
|
||||||
if (reader.TokenType != JsonToken.Comment)
|
while (reader.Read())
|
||||||
throw new Exception("Additional text found in JSON string after finishing deserializing object.");
|
{
|
||||||
|
if (reader.TokenType != JsonToken.Comment)
|
||||||
|
throw new Exception("Additional text found in JSON string after finishing deserializing object.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cache.ReadEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -209,22 +253,20 @@ namespace FlaxEngine.Json
|
|||||||
{
|
{
|
||||||
object result;
|
object result;
|
||||||
var cache = Cache.Value;
|
var cache = Cache.Value;
|
||||||
cache.IsDuringSerialization = false;
|
cache.ReadBegin();
|
||||||
Current.Value = cache;
|
|
||||||
|
|
||||||
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
|
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
|
||||||
{
|
{
|
||||||
result = cache.JsonSerializer.Deserialize(reader, objectType);
|
result = cache.JsonSerializer.Deserialize(reader, objectType);
|
||||||
|
if (cache.JsonSerializer.CheckAdditionalContent)
|
||||||
if (!cache.JsonSerializer.CheckAdditionalContent)
|
|
||||||
return result;
|
|
||||||
while (reader.Read())
|
|
||||||
{
|
{
|
||||||
if (reader.TokenType != JsonToken.Comment)
|
while (reader.Read())
|
||||||
throw new Exception("Additional text found in JSON string after finishing deserializing object.");
|
{
|
||||||
|
if (reader.TokenType != JsonToken.Comment)
|
||||||
|
throw new Exception("Additional text found in JSON string after finishing deserializing object.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cache.ReadEnd();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,22 +279,21 @@ namespace FlaxEngine.Json
|
|||||||
{
|
{
|
||||||
object result;
|
object result;
|
||||||
var cache = Cache.Value;
|
var cache = Cache.Value;
|
||||||
cache.IsDuringSerialization = false;
|
cache.ReadBegin();
|
||||||
Current.Value = cache;
|
|
||||||
|
|
||||||
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
|
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
|
||||||
{
|
{
|
||||||
result = cache.JsonSerializer.Deserialize(reader);
|
result = cache.JsonSerializer.Deserialize(reader);
|
||||||
|
|
||||||
if (!cache.JsonSerializer.CheckAdditionalContent)
|
if (cache.JsonSerializer.CheckAdditionalContent)
|
||||||
return result;
|
|
||||||
while (reader.Read())
|
|
||||||
{
|
{
|
||||||
if (reader.TokenType != JsonToken.Comment)
|
while (reader.Read())
|
||||||
throw new Exception("Additional text found in JSON string after finishing deserializing object.");
|
{
|
||||||
|
if (reader.TokenType != JsonToken.Comment)
|
||||||
|
throw new Exception("Additional text found in JSON string after finishing deserializing object.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cache.ReadEnd();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,8 +306,7 @@ namespace FlaxEngine.Json
|
|||||||
public static unsafe void Deserialize(object input, byte* jsonBuffer, int jsonLength)
|
public static unsafe void Deserialize(object input, byte* jsonBuffer, int jsonLength)
|
||||||
{
|
{
|
||||||
var cache = Cache.Value;
|
var cache = Cache.Value;
|
||||||
cache.IsDuringSerialization = false;
|
cache.ReadBegin();
|
||||||
Current.Value = cache;
|
|
||||||
|
|
||||||
/*// Debug json string reading
|
/*// Debug json string reading
|
||||||
cache.MemoryStream.Initialize(jsonBuffer, jsonLength);
|
cache.MemoryStream.Initialize(jsonBuffer, jsonLength);
|
||||||
@@ -286,14 +326,16 @@ namespace FlaxEngine.Json
|
|||||||
{
|
{
|
||||||
cache.JsonSerializer.Populate(jsonReader, input);
|
cache.JsonSerializer.Populate(jsonReader, input);
|
||||||
}
|
}
|
||||||
|
if (cache.JsonSerializer.CheckAdditionalContent)
|
||||||
if (!cache.JsonSerializer.CheckAdditionalContent)
|
|
||||||
return;
|
|
||||||
while (jsonReader.Read())
|
|
||||||
{
|
{
|
||||||
if (jsonReader.TokenType != JsonToken.Comment)
|
while (jsonReader.Read())
|
||||||
throw new Exception("Additional text found in JSON string after finishing deserializing object.");
|
{
|
||||||
|
if (jsonReader.TokenType != JsonToken.Comment)
|
||||||
|
throw new Exception("Additional text found in JSON string after finishing deserializing object.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache.ReadEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ namespace Serialization
|
|||||||
Guid id;
|
Guid id;
|
||||||
Deserialize(stream, id, modifier);
|
Deserialize(stream, id, modifier);
|
||||||
modifier->IdsMapping.TryGet(id, id);
|
modifier->IdsMapping.TryGet(id, id);
|
||||||
v = (T*)FindObject(id, T::GetStaticClass());
|
v = (T*)::FindObject(id, T::GetStaticClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scripting Object Reference
|
// Scripting Object Reference
|
||||||
|
|||||||
@@ -245,4 +245,89 @@ TEST_CASE("Prefabs")
|
|||||||
Content::DeleteAsset(prefabA);
|
Content::DeleteAsset(prefabA);
|
||||||
Content::DeleteAsset(prefabB);
|
Content::DeleteAsset(prefabB);
|
||||||
}
|
}
|
||||||
|
SECTION("Test Syncing Changes In Nested Prefab Instance")
|
||||||
|
{
|
||||||
|
// https://github.com/FlaxEngine/FlaxEngine/issues/1015
|
||||||
|
|
||||||
|
// Create TestActor prefab with just root object
|
||||||
|
AssetReference<Prefab> testActorPrefab = Content::CreateVirtualAsset<Prefab>();
|
||||||
|
REQUIRE(testActorPrefab);
|
||||||
|
Guid id;
|
||||||
|
Guid::Parse("7691e981482f2a486e10cfae149e07d3", id);
|
||||||
|
testActorPrefab->ChangeID(id);
|
||||||
|
auto testActorPrefabInit = testActorPrefab->Init(Prefab::TypeName,
|
||||||
|
"["
|
||||||
|
"{"
|
||||||
|
"\"ID\": \"5d73990240497afc0c6d36814cc6ebbe\","
|
||||||
|
"\"TypeName\": \"FlaxEngine.EmptyActor\","
|
||||||
|
"\"Name\": \"TestActor\""
|
||||||
|
"}"
|
||||||
|
"]");
|
||||||
|
REQUIRE(!testActorPrefabInit);
|
||||||
|
|
||||||
|
// Create NestedActor prefab that inherits from TestActor prefab
|
||||||
|
AssetReference<Prefab> nestedActorPrefab = Content::CreateVirtualAsset<Prefab>();
|
||||||
|
REQUIRE(nestedActorPrefab);
|
||||||
|
Guid::Parse("1d521df4465ad849e274748c6d14b703", id);
|
||||||
|
nestedActorPrefab->ChangeID(id);
|
||||||
|
auto nestedActorPrefabInit = nestedActorPrefab->Init(Prefab::TypeName,
|
||||||
|
"["
|
||||||
|
"{"
|
||||||
|
"\"ID\": \"75c1587b4caeea27241ba7af00dafd45\","
|
||||||
|
"\"PrefabID\": \"7691e981482f2a486e10cfae149e07d3\","
|
||||||
|
"\"PrefabObjectID\": \"5d73990240497afc0c6d36814cc6ebbe\","
|
||||||
|
"\"Name\": \"NestedActor\""
|
||||||
|
"}"
|
||||||
|
"]");
|
||||||
|
REQUIRE(!nestedActorPrefabInit);
|
||||||
|
|
||||||
|
// Spawn test instances of both prefabs
|
||||||
|
ScriptingObjectReference<Actor> testActor = PrefabManager::SpawnPrefab(testActorPrefab);
|
||||||
|
ScriptingObjectReference<Actor> nestedActor = PrefabManager::SpawnPrefab(nestedActorPrefab);
|
||||||
|
|
||||||
|
// Verify initial scenario
|
||||||
|
REQUIRE(testActor);
|
||||||
|
CHECK(testActor->GetName() == TEXT("TestActor"));
|
||||||
|
CHECK(testActor->GetStaticFlags() == StaticFlags::FullyStatic);
|
||||||
|
REQUIRE(nestedActor);
|
||||||
|
CHECK(nestedActor->GetName() == TEXT("NestedActor"));
|
||||||
|
CHECK(nestedActor->GetStaticFlags() == StaticFlags::FullyStatic);
|
||||||
|
|
||||||
|
// Modify TestActor instance to have Static Flags property changed
|
||||||
|
testActor->SetStaticFlags(StaticFlags::None);
|
||||||
|
|
||||||
|
// Apply prefab changes
|
||||||
|
auto applyResult = PrefabManager::ApplyAll(testActor);
|
||||||
|
REQUIRE(!applyResult);
|
||||||
|
|
||||||
|
// Verify if instances were properly updated
|
||||||
|
REQUIRE(testActor);
|
||||||
|
CHECK(testActor->GetName() == TEXT("TestActor"));
|
||||||
|
CHECK(testActor->GetStaticFlags() == StaticFlags::None);
|
||||||
|
REQUIRE(nestedActor);
|
||||||
|
CHECK(nestedActor->GetName() == TEXT("NestedActor"));
|
||||||
|
CHECK(nestedActor->GetStaticFlags() == StaticFlags::None);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
nestedActor->DeleteObject();
|
||||||
|
testActor->DeleteObject();
|
||||||
|
|
||||||
|
// Spawn another test instances instances of both prefabs
|
||||||
|
testActor = PrefabManager::SpawnPrefab(testActorPrefab);
|
||||||
|
nestedActor = PrefabManager::SpawnPrefab(nestedActorPrefab);
|
||||||
|
|
||||||
|
// Verify if instances were properly updated
|
||||||
|
REQUIRE(testActor);
|
||||||
|
CHECK(testActor->GetName() == TEXT("TestActor"));
|
||||||
|
CHECK(testActor->GetStaticFlags() == StaticFlags::None);
|
||||||
|
REQUIRE(nestedActor);
|
||||||
|
CHECK(nestedActor->GetName() == TEXT("NestedActor"));
|
||||||
|
CHECK(nestedActor->GetStaticFlags() == StaticFlags::None);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
nestedActor->DeleteObject();
|
||||||
|
testActor->DeleteObject();
|
||||||
|
Content::DeleteAsset(nestedActorPrefab);
|
||||||
|
Content::DeleteAsset(testActorPrefab);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1275,16 +1275,16 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
|
|||||||
// Random Float Range
|
// Random Float Range
|
||||||
case 213:
|
case 213:
|
||||||
{
|
{
|
||||||
auto& a = node->Values[0].AsFloat;
|
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat;
|
||||||
auto& b = node->Values[1].AsFloat;
|
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat;
|
||||||
value = Math::Lerp(a, b, RAND);
|
value = Math::Lerp(a, b, RAND);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Random Vector2 Range
|
// Random Vector2 Range
|
||||||
case 214:
|
case 214:
|
||||||
{
|
{
|
||||||
auto a = (Float2)node->Values[0];
|
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat2();
|
||||||
auto b = (Float2)node->Values[1];
|
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat2();
|
||||||
value = Float2(
|
value = Float2(
|
||||||
Math::Lerp(a.X, b.X, RAND),
|
Math::Lerp(a.X, b.X, RAND),
|
||||||
Math::Lerp(a.Y, b.Y, RAND)
|
Math::Lerp(a.Y, b.Y, RAND)
|
||||||
@@ -1294,8 +1294,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
|
|||||||
// Random Vector3 Range
|
// Random Vector3 Range
|
||||||
case 215:
|
case 215:
|
||||||
{
|
{
|
||||||
auto a = (Float3)node->Values[0];
|
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat3();
|
||||||
auto b = (Float3)node->Values[1];
|
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat3();
|
||||||
value = Float3(
|
value = Float3(
|
||||||
Math::Lerp(a.X, b.X, RAND),
|
Math::Lerp(a.X, b.X, RAND),
|
||||||
Math::Lerp(a.Y, b.Y, RAND),
|
Math::Lerp(a.Y, b.Y, RAND),
|
||||||
@@ -1306,8 +1306,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
|
|||||||
// Random Vector4 Range
|
// Random Vector4 Range
|
||||||
case 216:
|
case 216:
|
||||||
{
|
{
|
||||||
auto a = (Float4)node->Values[0];
|
auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat4();
|
||||||
auto b = (Float4)node->Values[1];
|
auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat4();
|
||||||
value = Float4(
|
value = Float4(
|
||||||
Math::Lerp(a.X, b.X, RAND),
|
Math::Lerp(a.X, b.X, RAND),
|
||||||
Math::Lerp(a.Y, b.Y, RAND),
|
Math::Lerp(a.Y, b.Y, RAND),
|
||||||
|
|||||||
@@ -27,36 +27,31 @@ VS2PS VS(float3 Position : POSITION, float4 Color : COLOR)
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerformDepthTest(float4 svPosition)
|
META_PS(true, FEATURE_LEVEL_ES2)
|
||||||
|
META_PERMUTATION_2(USE_DEPTH_TEST=0,USE_FAKE_LIGHTING=0)
|
||||||
|
META_PERMUTATION_2(USE_DEPTH_TEST=0,USE_FAKE_LIGHTING=1)
|
||||||
|
META_PERMUTATION_2(USE_DEPTH_TEST=1,USE_FAKE_LIGHTING=0)
|
||||||
|
META_PERMUTATION_2(USE_DEPTH_TEST=1,USE_FAKE_LIGHTING=1)
|
||||||
|
float4 PS(VS2PS input) : SV_Target
|
||||||
{
|
{
|
||||||
|
#if USE_DEPTH_TEST
|
||||||
// Depth test manually if compositing editor primitives
|
// Depth test manually if compositing editor primitives
|
||||||
FLATTEN
|
FLATTEN
|
||||||
if (EnableDepthTest)
|
if (EnableDepthTest)
|
||||||
{
|
{
|
||||||
float sceneDepthDeviceZ = SceneDepthTexture.Load(int3(svPosition.xy, 0)).r;
|
float sceneDepthDeviceZ = SceneDepthTexture.Load(int3(input.Position.xy, 0)).r;
|
||||||
float interpolatedDeviceZ = svPosition.z;
|
float interpolatedDeviceZ = input.Position.z;
|
||||||
clip(sceneDepthDeviceZ - interpolatedDeviceZ);
|
clip(sceneDepthDeviceZ - interpolatedDeviceZ);
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
|
|
||||||
float4 PerformFakeLighting(float4 svPosition, float4 c)
|
float4 color = input.Color;
|
||||||
{
|
#if USE_FAKE_LIGHTING
|
||||||
// Reconstruct view normal and calculate faked lighting
|
// Reconstruct view normal and calculate faked lighting
|
||||||
float depth = svPosition.z * 10000;
|
float depth = input.Position.z * 10000;
|
||||||
float3 n = normalize(float3(-ddx(depth), -ddy(depth), 1.0f));
|
float3 n = normalize(float3(-ddx(depth), -ddy(depth), 1.0f));
|
||||||
c.rgb *= saturate(abs(dot(n, float3(0, 1, 0))) + 0.5f);
|
color.rgb *= saturate(abs(dot(n, float3(0, 1, 0))) + 0.5f);
|
||||||
return c;
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
META_PS(true, FEATURE_LEVEL_ES2)
|
return color;
|
||||||
float4 PS(VS2PS input) : SV_Target
|
|
||||||
{
|
|
||||||
return PerformFakeLighting(input.Position, input.Color);
|
|
||||||
}
|
|
||||||
|
|
||||||
META_PS(true, FEATURE_LEVEL_ES2)
|
|
||||||
float4 PS_DepthTest(VS2PS input) : SV_Target
|
|
||||||
{
|
|
||||||
PerformDepthTest(input.Position);
|
|
||||||
return PerformFakeLighting(input.Position, input.Color);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user