Merge remote-tracking branch 'origin/master' into 1.11
# Conflicts: # Source/Engine/Level/Scene/SceneRendering.cpp # Source/Engine/Physics/Colliders/Collider.cpp # Source/Engine/Physics/Colliders/Collider.h
This commit is contained in:
@@ -749,6 +749,15 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
}
|
||||
|
||||
private Actor FindActor(CustomEditor editor)
|
||||
{
|
||||
if (editor.Values[0] is Actor actor)
|
||||
return actor;
|
||||
if (editor.ParentEditor != null)
|
||||
return FindActor(editor.ParentEditor);
|
||||
return null;
|
||||
}
|
||||
|
||||
private Actor FindPrefabRoot(CustomEditor editor)
|
||||
{
|
||||
if (editor.Values[0] is Actor actor)
|
||||
@@ -767,32 +776,35 @@ namespace FlaxEditor.CustomEditors
|
||||
return FindPrefabRoot(actor.Parent);
|
||||
}
|
||||
|
||||
private SceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId)
|
||||
private SceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId, Actor endPoint)
|
||||
{
|
||||
var visited = new HashSet<Actor>();
|
||||
return FindObjectWithPrefabObjectId(actor, ref prefabObjectId, endPoint, visited);
|
||||
}
|
||||
|
||||
private SceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId, Actor endPoint, HashSet<Actor> visited)
|
||||
{
|
||||
if (visited.Contains(actor) || actor is Scene || actor == endPoint)
|
||||
return null;
|
||||
if (actor.PrefabObjectID == prefabObjectId)
|
||||
return actor;
|
||||
|
||||
for (int i = 0; i < actor.ScriptsCount; i++)
|
||||
{
|
||||
if (actor.GetScript(i).PrefabObjectID == prefabObjectId)
|
||||
{
|
||||
var a = actor.GetScript(i);
|
||||
if (a != null)
|
||||
return a;
|
||||
}
|
||||
var script = actor.GetScript(i);
|
||||
if (script != null && script.PrefabObjectID == prefabObjectId)
|
||||
return script;
|
||||
}
|
||||
|
||||
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||
{
|
||||
if (actor.GetChild(i).PrefabObjectID == prefabObjectId)
|
||||
{
|
||||
var a = actor.GetChild(i);
|
||||
if (a != null)
|
||||
return a;
|
||||
}
|
||||
var child = actor.GetChild(i);
|
||||
if (child != null && child.PrefabObjectID == prefabObjectId)
|
||||
return child;
|
||||
}
|
||||
|
||||
return null;
|
||||
// Go up in the hierarchy
|
||||
return FindObjectWithPrefabObjectId(actor.Parent, ref prefabObjectId, endPoint, visited);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -826,7 +838,7 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
|
||||
var prefabObjectId = referenceSceneObject.PrefabObjectID;
|
||||
var prefabInstanceRef = FindObjectWithPrefabObjectId(prefabInstanceRoot, ref prefabObjectId);
|
||||
var prefabInstanceRef = FindObjectWithPrefabObjectId(FindActor(this), ref prefabObjectId, prefabInstanceRoot);
|
||||
if (prefabInstanceRef == null)
|
||||
{
|
||||
Editor.LogWarning("Missing prefab instance reference in the prefab instance. Cannot revert to it.");
|
||||
|
||||
@@ -63,6 +63,11 @@ namespace FlaxEditor.CustomEditors
|
||||
/// Indication of if the properties window is locked on specific objects.
|
||||
/// </summary>
|
||||
public bool LockSelection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scene editing context.
|
||||
/// </summary>
|
||||
public ISceneEditingContext SceneContext { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Actions;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
@@ -10,12 +7,14 @@ using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using FlaxEngine.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -240,6 +239,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
node.TextColor = Color.OrangeRed;
|
||||
node.Text = Utilities.Utils.GetPropertyNameUI(removed.PrefabObject.GetType().Name);
|
||||
}
|
||||
// Removed Actor
|
||||
else if (editor is RemovedActorDummy removedActor)
|
||||
{
|
||||
node.TextColor = Color.OrangeRed;
|
||||
node.Text = $"{removedActor.PrefabObject.Name} ({Utilities.Utils.GetPropertyNameUI(removedActor.PrefabObject.GetType().Name)})";
|
||||
}
|
||||
// Actor or Script
|
||||
else if (editor.Values[0] is SceneObject sceneObject)
|
||||
{
|
||||
@@ -295,16 +300,40 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Not used
|
||||
}
|
||||
}
|
||||
|
||||
private class RemovedActorDummy : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The removed prefab object (from the prefab default instance).
|
||||
/// </summary>
|
||||
public Actor PrefabObject;
|
||||
|
||||
/// <summary>
|
||||
/// The prefab instance's parent.
|
||||
/// </summary>
|
||||
public Actor ParentActor;
|
||||
|
||||
/// <summary>
|
||||
/// The order of the removed actor in the parent.
|
||||
/// </summary>
|
||||
public int OrderInParent;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
// Not used
|
||||
}
|
||||
}
|
||||
|
||||
private TreeNode ProcessDiff(CustomEditor editor, bool skipIfNotModified = true)
|
||||
{
|
||||
// Special case for new Script added to actor
|
||||
if (editor.Values[0] is Script script && !script.HasPrefabLink)
|
||||
// Special case for new Script or child actor added to actor
|
||||
if ((editor.Values[0] is Script script && !script.HasPrefabLink) || (editor.Values[0] is Actor a && !a.HasPrefabLink))
|
||||
return CreateDiffNode(editor);
|
||||
|
||||
// Skip if no change detected
|
||||
var isRefEdited = editor.Values.IsReferenceValueModified;
|
||||
if (!isRefEdited && skipIfNotModified)
|
||||
if (!isRefEdited && skipIfNotModified && editor is not ScriptsEditor)
|
||||
return null;
|
||||
|
||||
TreeNode result = null;
|
||||
@@ -359,6 +388,44 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
}
|
||||
|
||||
// Compare child actors for removed actors.
|
||||
if (editor is ActorEditor && editor.Values.HasReferenceValue && editor.Values.ReferenceValue is Actor prefabObjectActor)
|
||||
{
|
||||
var thisActor = editor.Values[0] as Actor;
|
||||
for (int i = 0; i < prefabObjectActor.ChildrenCount; i++)
|
||||
{
|
||||
var prefabActorChild = prefabObjectActor.Children[i];
|
||||
if (thisActor == null)
|
||||
continue;
|
||||
bool isRemoved = true;
|
||||
for (int j = 0; j < thisActor.ChildrenCount; j++)
|
||||
{
|
||||
var actorChild = thisActor.Children[j];
|
||||
if (actorChild.PrefabObjectID == prefabActorChild.PrefabObjectID)
|
||||
{
|
||||
isRemoved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isRemoved)
|
||||
{
|
||||
var dummy = new RemovedActorDummy
|
||||
{
|
||||
PrefabObject = prefabActorChild,
|
||||
ParentActor = thisActor,
|
||||
OrderInParent = prefabActorChild.OrderInParent,
|
||||
};
|
||||
var child = CreateDiffNode(dummy);
|
||||
if (result == null)
|
||||
result = CreateDiffNode(editor);
|
||||
result.AddChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (editor is ScriptsEditor && result != null && result.ChildrenCount == 0)
|
||||
return null;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -438,6 +505,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
Presenter.BuildLayoutOnUpdate();
|
||||
}
|
||||
|
||||
private static void GetAllPrefabObjects(List<object> objects, Actor actor)
|
||||
{
|
||||
objects.Add(actor);
|
||||
objects.AddRange(actor.Scripts);
|
||||
var children = actor.Children;
|
||||
foreach (var child in children)
|
||||
GetAllPrefabObjects(objects, child);
|
||||
}
|
||||
|
||||
private void OnDiffRevert(CustomEditor editor)
|
||||
{
|
||||
// Special case for removed Script from actor
|
||||
@@ -459,6 +535,22 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case for reverting removed Actors
|
||||
if (editor is RemovedActorDummy removedActor)
|
||||
{
|
||||
Editor.Log("Reverting removed actor changes to prefab (adding it)");
|
||||
|
||||
var parentActor = removedActor.ParentActor;
|
||||
var restored = parentActor.AddChild(removedActor.PrefabObject.GetType());
|
||||
var prefabId = parentActor.PrefabID;
|
||||
var prefabObjectId = removedActor.PrefabObject.PrefabObjectID;
|
||||
string data = JsonSerializer.Serialize(removedActor.PrefabObject);
|
||||
JsonSerializer.Deserialize(restored, data);
|
||||
Presenter.Owner.SceneContext.Spawn(restored, parentActor, removedActor.OrderInParent);
|
||||
Actor.Internal_LinkPrefab(FlaxEngine.Object.GetUnmanagedPtr(restored), ref prefabId, ref prefabObjectId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case for new Script added to actor
|
||||
if (editor.Values[0] is Script script && !script.HasPrefabLink)
|
||||
{
|
||||
@@ -470,8 +562,37 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case for new Actor added to actor
|
||||
if (editor.Values[0] is Actor a && !a.HasPrefabLink)
|
||||
{
|
||||
Editor.Log("Reverting added actor changes to prefab (removing it)");
|
||||
|
||||
editor.RevertToReferenceValue();
|
||||
// TODO: Keep previous selection.
|
||||
var context = Presenter.Owner.SceneContext;
|
||||
context.Select(SceneGraph.SceneGraphFactory.FindNode(a.ID));
|
||||
context.DeleteSelection();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Presenter.Undo != null && Presenter.Undo.Enabled)
|
||||
{
|
||||
var thisActor = (Actor)Values[0];
|
||||
var rootActor = thisActor.IsPrefabRoot ? thisActor : thisActor.GetPrefabRoot();
|
||||
var prefabObjects = new List<object>();
|
||||
GetAllPrefabObjects(prefabObjects, rootActor);
|
||||
using (new UndoMultiBlock(Presenter.Undo, prefabObjects, "Revert to Prefab"))
|
||||
{
|
||||
editor.RevertToReferenceValue();
|
||||
editor.RefreshInternal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
editor.RevertToReferenceValue();
|
||||
editor.RefreshInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
menu.ItemsContainer.RemoveChildren();
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
menu.AddSeparator();
|
||||
@@ -404,8 +406,10 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var menu = new ContextMenu();
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste;
|
||||
paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
if (_canReorder)
|
||||
{
|
||||
@@ -741,6 +745,34 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
cloned[srcIndex] = tmp;
|
||||
SetValue(cloned);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates the list item.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to duplicate.</param>
|
||||
public void Duplicate(int index)
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var count = Count;
|
||||
var newValues = Allocate(count + 1);
|
||||
var oldValues = (IList)Values[0];
|
||||
|
||||
for (int i = 0; i <= index; i++)
|
||||
{
|
||||
newValues[i] = oldValues[i];
|
||||
}
|
||||
|
||||
newValues[index + 1] = Utilities.Utils.CloneValue(oldValues[index]);
|
||||
|
||||
for (int i = index + 1; i < count; i++)
|
||||
{
|
||||
newValues[i + 1] = oldValues[i];
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shifts the specified item at the given index and moves it through the list to the other item. It supports undo.
|
||||
|
||||
@@ -254,18 +254,29 @@ namespace FlaxEditor.Modules
|
||||
PrefabApplying?.Invoke(prefab, instance);
|
||||
|
||||
// When applying changes to prefab from actor in level ignore it's root transformation (see ActorEditor.ProcessDiff)
|
||||
Actor prefabRoot = null;
|
||||
var originalTransform = instance.LocalTransform;
|
||||
var originalName = instance.Name;
|
||||
if (instance.IsPrefabRoot && instance.HasScene)
|
||||
if (instance.HasScene)
|
||||
{
|
||||
instance.LocalTransform = prefab.GetDefaultInstance().Transform;
|
||||
instance.Name = prefab.GetDefaultInstance().Name;
|
||||
prefabRoot = instance.GetPrefabRoot();
|
||||
if (prefabRoot != null && prefabRoot.IsPrefabRoot && instance.HasScene)
|
||||
{
|
||||
var defaultInstance = prefab.GetDefaultInstance();
|
||||
originalTransform = prefabRoot.LocalTransform;
|
||||
originalName = prefabRoot.Name;
|
||||
prefabRoot.LocalTransform = defaultInstance.Transform;
|
||||
prefabRoot.Name = defaultInstance.Name;
|
||||
}
|
||||
}
|
||||
|
||||
// Call backend
|
||||
var failed = PrefabManager.Internal_ApplyAll(FlaxEngine.Object.GetUnmanagedPtr(instance));
|
||||
instance.LocalTransform = originalTransform;
|
||||
instance.Name = originalName;
|
||||
if (prefabRoot != null)
|
||||
{
|
||||
prefabRoot.LocalTransform = originalTransform;
|
||||
prefabRoot.Name = originalName;
|
||||
}
|
||||
if (failed)
|
||||
throw new Exception("Failed to apply the prefab. See log to learn more.");
|
||||
|
||||
|
||||
@@ -569,6 +569,10 @@ namespace FlaxEditor.Modules
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if already added
|
||||
if (SceneGraphFactory.Nodes.ContainsKey(actor.ID))
|
||||
return;
|
||||
|
||||
var node = SceneGraphFactory.BuildActorNode(actor);
|
||||
if (node != null)
|
||||
{
|
||||
|
||||
@@ -141,6 +141,8 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
b.TooltipText = "Add a box collider to every selected model that will auto resize based on the model bounds.";
|
||||
b = menu.ContextMenu.AddButton("Sphere", () => OnAddCollider(window, CreateSphere));
|
||||
b.TooltipText = "Add a sphere collider to every selected model that will auto resize based on the model bounds.";
|
||||
b = menu.ContextMenu.AddButton("Capsule", () => OnAddCollider(window, CreateCapsule));
|
||||
b.TooltipText = "Add a capsule collider to every selected model that will auto resize based on the model bounds.";
|
||||
b = menu.ContextMenu.AddButton("Convex", () => OnAddCollider(window, CreateConvex));
|
||||
b.TooltipText = "Generate and add a convex collider for every selected model.";
|
||||
b = menu.ContextMenu.AddButton("Triangle Mesh", () => OnAddCollider(window, CreateTriangle));
|
||||
@@ -267,6 +269,20 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
spawner(collider);
|
||||
}
|
||||
|
||||
private void CreateCapsule(StaticModel actor, Spawner spawner, bool singleNode)
|
||||
{
|
||||
var collider = new CapsuleCollider
|
||||
{
|
||||
Transform = actor.Transform,
|
||||
Position = actor.Box.Center,
|
||||
|
||||
// Size the capsule to best fit the actor
|
||||
Radius = (float)actor.Sphere.Radius / Mathf.Max((float)actor.Scale.MaxValue, 0.0001f) * 0.707f,
|
||||
Height = 100f,
|
||||
};
|
||||
spawner(collider);
|
||||
}
|
||||
|
||||
private void CreateConvex(StaticModel actor, Spawner spawner, bool singleNode)
|
||||
{
|
||||
CreateMeshCollider(actor, spawner, singleNode, CollisionDataType.ConvexMesh);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Viewport;
|
||||
using FlaxEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlaxEditor
|
||||
{
|
||||
@@ -39,9 +40,23 @@ namespace FlaxEditor
|
||||
/// </summary>
|
||||
void RenameSelection();
|
||||
|
||||
/// <summary>
|
||||
/// Deletes selected objects.
|
||||
/// </summary>
|
||||
void DeleteSelection();
|
||||
|
||||
/// <summary>
|
||||
/// Focuses selected objects.
|
||||
/// </summary>
|
||||
void FocusSelection();
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the specified actor to the game (with undo).
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
/// <param name="parent">The parent actor. Set null as default.</param>
|
||||
/// <param name="orderInParent">The order under the parent to put the spawned actor.</param>
|
||||
/// <param name="autoSelect">True if automatically select the spawned actor, otherwise false.</param>
|
||||
void Spawn(Actor actor, Actor parent = null, int orderInParent = -1, bool autoSelect = true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ namespace FlaxEditor.SceneGraph
|
||||
public List<SceneGraphNode> Selection => SceneContext.Selection;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of selected scene graph nodes in the editor context.
|
||||
/// Gets the scene editing context.
|
||||
/// </summary>
|
||||
public abstract ISceneEditingContext SceneContext { get; }
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace FlaxEditor.Utilities
|
||||
/// <param name="value">The value.</param>
|
||||
public void SetMemberValue(object instance, object value)
|
||||
{
|
||||
var originalInstance = instance;
|
||||
var finalMember = MemberPath.GetLastMember(ref instance);
|
||||
|
||||
var type = finalMember.Type;
|
||||
@@ -92,6 +93,12 @@ namespace FlaxEditor.Utilities
|
||||
}
|
||||
|
||||
finalMember.SetValue(instance, value);
|
||||
|
||||
if (instance != originalInstance && finalMember.Index != null)
|
||||
{
|
||||
// Set collection back to the parent object (in case of properties that always return a new object like 'Spline.SplineKeyframes')
|
||||
finalMember.Member.SetValue(originalInstance, instance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -668,10 +668,7 @@ namespace FlaxEditor.Viewport
|
||||
if ((view.Flags & ViewFlags.PhysicsDebug) != 0 || view.Mode == ViewMode.PhysicsColliders)
|
||||
{
|
||||
foreach (var actor in _debugDrawActors)
|
||||
{
|
||||
if (actor is Collider c && c.IsActiveInHierarchy)
|
||||
DebugDraw.DrawColliderDebugPhysics(c, renderContext.View);
|
||||
}
|
||||
DebugDraw.DrawDebugPhysics(actor, renderContext.View);
|
||||
}
|
||||
|
||||
// Draw lights debug
|
||||
|
||||
@@ -641,5 +641,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool LockSelection { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ISceneEditingContext SceneContext => null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using FlaxEditor.Content.Create;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEngine;
|
||||
@@ -171,13 +172,49 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
|
||||
private readonly SplitPanel _split;
|
||||
private readonly ModelBasePreview _preview;
|
||||
private readonly CollisionDataPreview _preview;
|
||||
private readonly CustomEditorPresenter _propertiesPresenter;
|
||||
private readonly PropertiesProxy _properties;
|
||||
private Model _collisionWiresModel;
|
||||
private StaticModel _collisionWiresShowActor;
|
||||
private bool _updateWireMesh;
|
||||
|
||||
private class CollisionDataPreview : ModelBasePreview
|
||||
{
|
||||
public bool ShowCollisionData = false;
|
||||
private int _verticesCount = 0;
|
||||
private int _trianglesCount = 0;
|
||||
|
||||
public void SetVerticesAndTriangleCount(int verticesCount, int triangleCount)
|
||||
{
|
||||
_verticesCount = verticesCount;
|
||||
_trianglesCount = triangleCount;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public CollisionDataPreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
ViewportCamera = new FPSCamera();
|
||||
Task.ViewFlags &= ~ViewFlags.Sky & ~ViewFlags.Bloom & ~ViewFlags.EyeAdaptation;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
if (ShowCollisionData)
|
||||
{
|
||||
var text = string.Format("\nTriangles: {0:N0}\nVertices: {1:N0}\nMemory Size: {2}", _trianglesCount, _verticesCount, Utilities.Utils.FormatBytesCount(Asset.MemoryUsage));
|
||||
var font = Style.Current.FontMedium;
|
||||
var pos = new Float2(10, 50);
|
||||
Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black);
|
||||
Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public CollisionDataWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
@@ -185,6 +222,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole collision");
|
||||
var infoButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Info64).LinkTooltip("Show Collision Data");
|
||||
infoButton.Clicked += () =>
|
||||
{
|
||||
_preview.ShowCollisionData = !_preview.ShowCollisionData;
|
||||
infoButton.Checked = _preview.ShowCollisionData;
|
||||
};
|
||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/physics/colliders/collision-data.html")).LinkTooltip("See documentation to learn more");
|
||||
|
||||
// Split Panel
|
||||
@@ -197,12 +240,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
};
|
||||
|
||||
// Model preview
|
||||
_preview = new ModelBasePreview(true)
|
||||
_preview = new CollisionDataPreview(true)
|
||||
{
|
||||
ViewportCamera = new FPSCamera(),
|
||||
Parent = _split.Panel1
|
||||
};
|
||||
_preview.Task.ViewFlags &= ~ViewFlags.Sky & ~ViewFlags.Bloom & ~ViewFlags.EyeAdaptation;
|
||||
|
||||
// Asset properties
|
||||
_propertiesPresenter = new CustomEditorPresenter(null);
|
||||
@@ -240,7 +281,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
_collisionWiresModel = FlaxEngine.Content.CreateVirtualAsset<Model>();
|
||||
_collisionWiresModel.SetupLODs(new[] { 1 });
|
||||
}
|
||||
Editor.Internal_GetCollisionWires(FlaxEngine.Object.GetUnmanagedPtr(Asset), out var triangles, out var indices, out var _, out var _);
|
||||
Editor.Internal_GetCollisionWires(FlaxEngine.Object.GetUnmanagedPtr(Asset), out var triangles, out var indices, out var triangleCount, out var indicesCount);
|
||||
if (triangles != null && indices != null)
|
||||
_collisionWiresModel.LODs[0].Meshes[0].UpdateMesh(triangles, indices);
|
||||
else
|
||||
@@ -252,6 +293,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
_collisionWiresShowActor.Model = _collisionWiresModel;
|
||||
_collisionWiresShowActor.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.WiresDebugMaterial));
|
||||
_preview.SetVerticesAndTriangleCount(triangleCount, indicesCount / 3);
|
||||
_preview.Asset = FlaxEngine.Content.LoadAsync<ModelBase>(_asset.Options.Model);
|
||||
}
|
||||
|
||||
|
||||
@@ -430,6 +430,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DeleteSelection()
|
||||
{
|
||||
Delete();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void FocusSelection()
|
||||
{
|
||||
@@ -488,7 +494,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <param name="actor">The actor.</param>
|
||||
/// <param name="parent">The parent.</param>
|
||||
/// <param name="orderInParent">The order of the actor under the parent.</param>
|
||||
public void Spawn(Actor actor, Actor parent, int orderInParent = -1)
|
||||
/// <param name="autoSelect">True if automatically select the spawned actor, otherwise false.</param>
|
||||
public void Spawn(Actor actor, Actor parent, int orderInParent = -1, bool autoSelect = true)
|
||||
{
|
||||
if (actor == null)
|
||||
throw new ArgumentNullException(nameof(actor));
|
||||
@@ -514,8 +521,11 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Create undo action
|
||||
var action = new CustomDeleteActorsAction(new List<SceneGraphNode>(1) { actorNode }, true);
|
||||
Undo.AddAction(action);
|
||||
Focus();
|
||||
Select(actorNode);
|
||||
if (autoSelect)
|
||||
{
|
||||
Focus();
|
||||
Select(actorNode);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTreeRightClick(TreeNode node, Float2 location)
|
||||
|
||||
@@ -91,6 +91,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ISceneEditingContext SceneContext => this;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use live reloading for the prefab changes (applies prefab changes on modification by auto).
|
||||
/// </summary>
|
||||
|
||||
@@ -58,6 +58,9 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ISceneEditingContext SceneContext => Editor.Windows.EditWin;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertiesWindow"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -26,12 +26,24 @@ namespace FlaxEditor.Windows
|
||||
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DeleteSelection()
|
||||
{
|
||||
Editor.SceneEditing.Delete();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void FocusSelection()
|
||||
{
|
||||
Editor.Windows.EditWin.Viewport.FocusSelection();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Spawn(Actor actor, Actor parent = null, int orderInParent = -1, bool autoSelect = true)
|
||||
{
|
||||
Editor.SceneEditing.Spawn(actor, parent, orderInParent, autoSelect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EditorViewport Viewport => Editor.Windows.EditWin.Viewport;
|
||||
|
||||
|
||||
@@ -417,7 +417,7 @@ public:
|
||||
template<typename ItemType>
|
||||
bool Add(const ItemType& item)
|
||||
{
|
||||
Bucket* bucket = Base::OnAdd(item, false);
|
||||
Bucket* bucket = Base::OnAdd(item, false, true);
|
||||
if (bucket)
|
||||
bucket->Occupy(item);
|
||||
return bucket != nullptr;
|
||||
@@ -430,7 +430,7 @@ public:
|
||||
/// <returns>True if element has been added to the collection, otherwise false if the element is already present.</returns>
|
||||
bool Add(T&& item)
|
||||
{
|
||||
Bucket* bucket = Base::OnAdd(item, false);
|
||||
Bucket* bucket = Base::OnAdd(item, false, true);
|
||||
if (bucket)
|
||||
bucket->Occupy(MoveTemp(item));
|
||||
return bucket != nullptr;
|
||||
|
||||
@@ -365,7 +365,7 @@ protected:
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
BucketType* OnAdd(const KeyComparableType& key, bool checkUnique = true)
|
||||
BucketType* OnAdd(const KeyComparableType& key, bool checkUnique = true, bool nullIfNonUnique = false)
|
||||
{
|
||||
// Check if need to rehash elements (prevent many deleted elements that use too much of capacity)
|
||||
if (_deletedCount * HASH_SET_DEFAULT_SLACK_SCALE > _size)
|
||||
@@ -386,6 +386,8 @@ protected:
|
||||
Platform::CheckFailed("That key has been already added to the collection.", __FILE__, __LINE__);
|
||||
return nullptr;
|
||||
}
|
||||
if (nullIfNonUnique)
|
||||
return nullptr;
|
||||
return &_allocation.Get()[pos.ObjectIndex];
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "Editor/Editor.h"
|
||||
#include "Engine/Level/Actors/Light.h"
|
||||
#include "Engine/Physics/Colliders/Collider.h"
|
||||
#include "Engine/Physics/Actors/IPhysicsDebug.h"
|
||||
#endif
|
||||
|
||||
// Debug draw service configuration
|
||||
@@ -1041,11 +1042,12 @@ void DebugDraw::DrawActorsTree(Actor* actor)
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void DebugDraw::DrawColliderDebugPhysics(Collider* collider, RenderView& view)
|
||||
void DebugDraw::DrawDebugPhysics(Actor* actor, RenderView& view)
|
||||
{
|
||||
if (!collider)
|
||||
if (!actor || !actor->IsActiveInHierarchy())
|
||||
return;
|
||||
collider->DrawPhysicsDebug(view);
|
||||
if (auto* physicsDebug = dynamic_cast<IPhysicsDebug*>(actor))
|
||||
physicsDebug->DrawPhysicsDebug(view);
|
||||
}
|
||||
|
||||
void DebugDraw::DrawLightDebug(Light* light, RenderView& view)
|
||||
|
||||
@@ -107,11 +107,11 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Draws the physics debug shapes for the given collider. Editor Only
|
||||
/// Draws the physics debug shapes for the given actor. Editor Only
|
||||
/// </summary>
|
||||
/// <param name="collider">The collider to draw.</param>
|
||||
/// <param name="actor">The actor to draw.</param>
|
||||
/// <param name="view">The render view to draw in.</param>
|
||||
API_FUNCTION() static void DrawColliderDebugPhysics(Collider* collider, RenderView& view);
|
||||
API_FUNCTION() static void DrawDebugPhysics(Actor* actor, RenderView& view);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the light debug shapes for the given light. Editor Only
|
||||
|
||||
@@ -304,6 +304,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
||||
{
|
||||
for (int32 instanceIndex = 0; instanceIndex < prefabInstancesData.Count(); instanceIndex++)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Instance");
|
||||
auto& instance = prefabInstancesData[instanceIndex];
|
||||
ISerializeModifierCacheType modifier = Cache::ISerializeModifier.Get();
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
|
||||
@@ -377,6 +378,10 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
||||
sceneObjects->Add(obj);
|
||||
}
|
||||
|
||||
// Generate nested prefab instances to properly handle Ids Mapping within each nested prefab
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, instance.Data, modifier.Value);
|
||||
SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData);
|
||||
|
||||
// Apply modifications
|
||||
for (int32 i = existingObjectsCount - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -388,6 +393,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
||||
if (prefabObjectIdToDiffData.TryGet(obj->GetPrefabObjectID(), data))
|
||||
{
|
||||
// Apply prefab changes
|
||||
context.SetupIdsMapping(obj, modifier.Value);
|
||||
obj->Deserialize(*(ISerializable::DeserializeStream*)data, modifier.Value);
|
||||
}
|
||||
else
|
||||
@@ -424,7 +430,6 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
|
||||
int32 dataIndex;
|
||||
if (instance.PrefabInstanceIdToDataIndex.TryGet(obj->GetSceneObjectId(), dataIndex))
|
||||
{
|
||||
@@ -440,6 +445,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
||||
data.RemoveMember("ParentID");
|
||||
#endif
|
||||
|
||||
context.SetupIdsMapping(obj, modifier.Value);
|
||||
obj->Deserialize(data, modifier.Value);
|
||||
|
||||
// Preserve order in parent (values from prefab are used)
|
||||
@@ -527,6 +533,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
||||
{
|
||||
if (prefabInstancesData.IsEmpty())
|
||||
return false;
|
||||
PROFILE_CPU();
|
||||
|
||||
// Fully serialize default instance scene objects (accumulate all prefab and nested prefabs changes into a single linear list of objects)
|
||||
rapidjson_flax::Document defaultInstanceData;
|
||||
@@ -926,7 +933,6 @@ 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?
|
||||
JsonTools::ChangeIds(diffDataDocument, objectInstanceIdToPrefabObjectId);
|
||||
}
|
||||
dataBuffer.Clear();
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||
|
||||
// Destroy default instance and some cache data in Prefab
|
||||
@@ -1002,6 +1008,32 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
obj->RegisterObject();
|
||||
}
|
||||
|
||||
// Generate nested prefab instances to properly handle Ids Mapping within each nested prefab
|
||||
rapidjson_flax::Document targetDataDocument;
|
||||
if (NestedPrefabs.HasItems())
|
||||
{
|
||||
targetDataDocument.Parse(dataBuffer.GetString(), dataBuffer.GetSize());
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, targetDataDocument, modifier.Value);
|
||||
SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData);
|
||||
|
||||
if (context.Instances.HasItems())
|
||||
{
|
||||
// Only main prefab instance is allowed (in case nested prefab was added to this prefab)
|
||||
for (auto i = context.ObjectToInstance.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value != 0)
|
||||
context.ObjectToInstance.Remove(i);
|
||||
}
|
||||
context.Instances.Resize(1);
|
||||
|
||||
// Trash object mapping to prevent messing up prefab structure when applying hierarchy changes (only nested instances are used)
|
||||
context.Instances[0].IdsMapping.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
dataBuffer.Clear();
|
||||
auto originalIdsMapping = modifier.Value->IdsMapping;
|
||||
|
||||
// Deserialize prefab objects and apply modifications
|
||||
for (int32 i = 0; i < ObjectsCount; i++)
|
||||
{
|
||||
@@ -1037,6 +1069,9 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
}
|
||||
}
|
||||
|
||||
// Use the initial Ids Mapping (SetupIdsMapping overrides it for instanced prefabs)
|
||||
modifier.Value->IdsMapping = originalIdsMapping;
|
||||
|
||||
// Deserialize new prefab objects
|
||||
newPrefabInstanceIdToDataIndexCounter = 0;
|
||||
for (auto i = newPrefabInstanceIdToDataIndex.Begin(); i.IsNotEnd(); ++i)
|
||||
@@ -1283,6 +1318,10 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
||||
ObjectsDataCache.Add(objectId, &objData);
|
||||
ObjectsCount++;
|
||||
|
||||
Guid parentID;
|
||||
if (JsonTools::GetGuidIfValid(parentID, objData, "ParentID"))
|
||||
ObjectsHierarchyCache[parentID].Add(objectId);
|
||||
|
||||
Guid prefabId = JsonTools::GetGuid(objData, "PrefabID");
|
||||
if (prefabId.IsValid() && !NestedPrefabs.Contains(prefabId))
|
||||
{
|
||||
|
||||
@@ -172,7 +172,9 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const SpawnOptions& options)
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||
}
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, data, modifier.Value);
|
||||
if (options.WithSync)
|
||||
bool withSync = options.WithSync || prefab->NestedPrefabs.HasItems(); // Nested prefabs needs prefab instances generation for correct IdsMapping if the same prefab exists multiple times
|
||||
// TODO: let prefab check if has multiple nested prefabs at cook time?
|
||||
if (withSync)
|
||||
{
|
||||
// Synchronize new prefab instances (prefab may have new objects added so deserialized instances need to synchronize with it)
|
||||
// TODO: resave and force sync prefabs during game cooking so this step could be skipped in game
|
||||
@@ -187,14 +189,14 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const SpawnOptions& options)
|
||||
if (obj)
|
||||
SceneObjectsFactory::Deserialize(context, obj, stream);
|
||||
}
|
||||
Scripting::ObjectsLookupIdMapping.Set(prevIdMapping);
|
||||
|
||||
// Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it)
|
||||
if (options.WithSync)
|
||||
if (withSync)
|
||||
{
|
||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(context, prefabSyncData);
|
||||
}
|
||||
Scripting::ObjectsLookupIdMapping.Set(prevIdMapping);
|
||||
|
||||
// Pick prefab root object
|
||||
Actor* root = nullptr;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Threading/JobSystem.h"
|
||||
#include "Engine/Physics/Actors/IPhysicsDebug.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#if !BUILD_RELEASE
|
||||
@@ -107,10 +108,10 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
|
||||
if (EnumHasAnyFlags(view.Flags, ViewFlags::PhysicsDebug) || view.Mode == ViewMode::PhysicsColliders)
|
||||
{
|
||||
PROFILE_CPU_NAMED("PhysicsDebug");
|
||||
const PhysicsDebugCallback* physicsDebugData = PhysicsDebug.Get();
|
||||
const auto* physicsDebugData = PhysicsDebug.Get();
|
||||
for (int32 i = 0; i < PhysicsDebug.Count(); i++)
|
||||
{
|
||||
physicsDebugData[i](view);
|
||||
physicsDebugData[i]->DrawPhysicsDebug(view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
class SceneRenderTask;
|
||||
class SceneRendering;
|
||||
class IPhysicsDebug;
|
||||
struct PostProcessSettings;
|
||||
struct RenderContext;
|
||||
struct RenderContextBatch;
|
||||
@@ -74,7 +75,6 @@ public:
|
||||
class FLAXENGINE_API SceneRendering
|
||||
{
|
||||
#if USE_EDITOR
|
||||
typedef Function<void(RenderView&)> PhysicsDebugCallback;
|
||||
typedef Function<void(RenderView&)> LightsDebugCallback;
|
||||
friend class ViewportIconsRendererService;
|
||||
#endif
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
|
||||
private:
|
||||
#if USE_EDITOR
|
||||
Array<PhysicsDebugCallback> PhysicsDebug;
|
||||
Array<IPhysicsDebug*> PhysicsDebug;
|
||||
Array<LightsDebugCallback> LightsDebug;
|
||||
Array<Actor*> ViewportIcons;
|
||||
#endif
|
||||
@@ -150,20 +150,14 @@ public:
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
template<class T, void(T::*Method)(RenderView&)>
|
||||
FORCE_INLINE void AddPhysicsDebug(T* obj)
|
||||
FORCE_INLINE void AddPhysicsDebug(IPhysicsDebug* obj)
|
||||
{
|
||||
PhysicsDebugCallback f;
|
||||
f.Bind<T, Method>(obj);
|
||||
PhysicsDebug.Add(f);
|
||||
PhysicsDebug.Add(obj);
|
||||
}
|
||||
|
||||
template<class T, void(T::*Method)(RenderView&)>
|
||||
void RemovePhysicsDebug(T* obj)
|
||||
FORCE_INLINE void RemovePhysicsDebug(IPhysicsDebug* obj)
|
||||
{
|
||||
PhysicsDebugCallback f;
|
||||
f.Bind<T, Method>(obj);
|
||||
PhysicsDebug.Remove(f);
|
||||
PhysicsDebug.Remove(obj);
|
||||
}
|
||||
|
||||
template<class T, void(T::*Method)(RenderView&)>
|
||||
|
||||
@@ -101,13 +101,25 @@ ISerializeModifier* SceneObjectsFactory::Context::GetModifier()
|
||||
void SceneObjectsFactory::Context::SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier) const
|
||||
{
|
||||
int32 instanceIndex;
|
||||
if (ObjectToInstance.TryGet(obj->GetID(), instanceIndex) && instanceIndex != modifier->CurrentInstance)
|
||||
const Guid id = obj->GetID();
|
||||
if (ObjectToInstance.TryGet(id, instanceIndex))
|
||||
{
|
||||
// Apply the current prefab instance objects ids table to resolve references inside a prefab properly
|
||||
modifier->CurrentInstance = instanceIndex;
|
||||
const auto& instance = Instances[instanceIndex];
|
||||
for (const auto& e : instance.IdsMapping)
|
||||
modifier->IdsMapping[e.Key] = e.Value;
|
||||
if (instanceIndex != modifier->CurrentInstance)
|
||||
{
|
||||
modifier->CurrentInstance = instanceIndex;
|
||||
for (const auto& e : instance.IdsMapping)
|
||||
modifier->IdsMapping[e.Key] = e.Value;
|
||||
}
|
||||
int32 nestedIndex;
|
||||
if (instance.ObjectToNested.TryGet(id, nestedIndex))
|
||||
{
|
||||
// Each nested prefab has own object ids mapping that takes precedence
|
||||
const auto& nested = instance.Nested[nestedIndex];
|
||||
for (const auto& e : nested.IdsMapping)
|
||||
modifier->IdsMapping[e.Key] = e.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,6 +511,34 @@ void SceneObjectsFactory::SetupPrefabInstances(Context& context, const PrefabSyn
|
||||
prefab = Content::LoadAsync<Prefab>(prefabId);
|
||||
if (prefab && !prefab->WaitForLoaded())
|
||||
{
|
||||
// If prefab instance contains multiple nested prefabs, then need to build separate Ids remapping for each of them to prevent collisions
|
||||
int32 nestedIndex = -1;
|
||||
if (prefabInstance.Nested.HasItems())
|
||||
{
|
||||
// Check only the last instance if the current object belongs to it
|
||||
for (int32 j = prefabInstance.Nested.Count() - 1; j >= 0; j--)
|
||||
{
|
||||
const auto& e = prefabInstance.Nested[j];
|
||||
if (e.Prefab == prefab && e.RootObjectId != prefabObjectId)
|
||||
{
|
||||
nestedIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nestedIndex == -1)
|
||||
{
|
||||
nestedIndex = prefabInstance.Nested.Count();
|
||||
auto& e = prefabInstance.Nested.AddOne();
|
||||
e.Prefab = prefab;
|
||||
e.RootObjectId = prefabObjectId;
|
||||
}
|
||||
|
||||
// Map this object into this instance to inherit all objects from it when looking up via IdsMapping
|
||||
prefabInstance.ObjectToNested[id] = nestedIndex;
|
||||
auto& nestedInstance = prefabInstance.Nested[nestedIndex];
|
||||
nestedInstance.IdsMapping[prefabObjectId] = id;
|
||||
|
||||
// Map prefab object ID to the deserialized instance ID
|
||||
prefabInstance.IdsMapping[prefabObjectId] = id;
|
||||
goto NESTED_PREFAB_WALK;
|
||||
@@ -794,23 +834,20 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, Prefab
|
||||
if (spawned)
|
||||
continue;
|
||||
|
||||
// Map prefab object ID to this actor's prefab instance so the new objects gets added to it
|
||||
// Map prefab object ID to this actor's prefab instance so the new objects get added to it
|
||||
context.SetupIdsMapping(actor, data.Modifier);
|
||||
data.Modifier->IdsMapping[actorPrefabObjectId] = actorId;
|
||||
Scripting::ObjectsLookupIdMapping.Set(&data.Modifier->IdsMapping);
|
||||
|
||||
// Create instance (including all children)
|
||||
SynchronizeNewPrefabInstance(context, data, prefab, actor, prefabObjectId);
|
||||
SynchronizeNewPrefabInstance(context, data, prefab, actor, prefabObjectId, actor->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::SynchronizeNewPrefabInstance(Context& context, PrefabSyncData& data, Prefab* prefab, Actor* actor, const Guid& prefabObjectId)
|
||||
void SceneObjectsFactory::SynchronizeNewPrefabInstance(Context& context, PrefabSyncData& data, Prefab* prefab, Actor* actor, const Guid& prefabObjectId, const Guid& nestedInstanceId)
|
||||
{
|
||||
PROFILE_CPU_NAMED("SynchronizeNewPrefabInstance");
|
||||
|
||||
// Missing object found!
|
||||
LOG(Info, "Actor {0} has missing child object (PrefabObjectID: {1}, PrefabID: {2}, Path: {3})", actor->ToString(), prefabObjectId, prefab->GetID(), prefab->GetPath());
|
||||
|
||||
// Get prefab object data from the prefab
|
||||
const ISerializable::DeserializeStream* prefabData;
|
||||
if (!prefab->ObjectsDataCache.TryGet(prefabObjectId, prefabData))
|
||||
@@ -830,6 +867,7 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstance(Context& context, PrefabS
|
||||
LOG(Warning, "Failed to create object {1} from prefab {0}.", prefab->ToString(), prefabObjectId);
|
||||
return;
|
||||
}
|
||||
LOG(Info, "Actor {0} has missing child object '{4}' (PrefabObjectID: {1}, PrefabID: {2}, Path: {3})", actor->ToString(), prefabObjectId, prefab->GetID(), prefab->GetPath(), child->GetType().ToString());
|
||||
|
||||
// Register object
|
||||
child->RegisterObject();
|
||||
@@ -846,17 +884,51 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstance(Context& context, PrefabS
|
||||
context.ObjectToInstance[id] = instanceIndex;
|
||||
auto& prefabInstance = context.Instances[instanceIndex];
|
||||
prefabInstance.IdsMapping[prefabObjectId] = id;
|
||||
|
||||
// Check if it's a nested prefab
|
||||
const ISerializable::DeserializeStream* nestedPrefabData;
|
||||
Guid nestedPrefabObjectId;
|
||||
if (prefab->ObjectsDataCache.TryGet(prefabObjectId, nestedPrefabData) && JsonTools::GetGuidIfValid(nestedPrefabObjectId, *nestedPrefabData, "PrefabObjectID"))
|
||||
{
|
||||
// Try reusing parent nested instance (or make a new one)
|
||||
int32 nestedIndex = -1;
|
||||
if (!prefabInstance.ObjectToNested.TryGet(nestedInstanceId, nestedIndex))
|
||||
{
|
||||
if (auto* nestedPrefab = Content::LoadAsync<Prefab>(JsonTools::GetGuid(*prefabData, "PrefabID")))
|
||||
{
|
||||
if (prefabInstance.Nested.HasItems())
|
||||
{
|
||||
// Check only the last instance if the current object belongs to it
|
||||
const auto& e = prefabInstance.Nested.Last();
|
||||
if (e.Prefab == nestedPrefab && e.RootObjectId != nestedPrefabObjectId)
|
||||
nestedIndex = prefabInstance.Nested.Count() - 1;
|
||||
}
|
||||
if (nestedIndex == -1)
|
||||
{
|
||||
nestedIndex = prefabInstance.Nested.Count();
|
||||
auto& e = prefabInstance.Nested.AddOne();
|
||||
e.Prefab = nestedPrefab;
|
||||
e.RootObjectId = nestedPrefabObjectId;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nestedIndex != -1)
|
||||
{
|
||||
// Insert into nested instance
|
||||
prefabInstance.ObjectToNested[id] = nestedIndex;
|
||||
auto& nestedInstance = prefabInstance.Nested[nestedIndex];
|
||||
nestedInstance.IdsMapping[nestedPrefabObjectId] = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use loop to add even more objects to added objects (prefab can have one new object that has another child, we need to add that child)
|
||||
// TODO: prefab could cache lookup object id -> children ids
|
||||
for (auto q = prefab->ObjectsDataCache.Begin(); q.IsNotEnd(); ++q)
|
||||
const auto* hierarchy = prefab->ObjectsHierarchyCache.TryGet(prefabObjectId);
|
||||
if (hierarchy)
|
||||
{
|
||||
Guid qParentId;
|
||||
if (JsonTools::GetGuidIfValid(qParentId, *q->Value, "ParentID") && qParentId == prefabObjectId)
|
||||
for (const Guid& e : *hierarchy)
|
||||
{
|
||||
const Guid qPrefabObjectId = JsonTools::GetGuid(*q->Value, "ID");
|
||||
SynchronizeNewPrefabInstance(context, data, prefab, actor, qPrefabObjectId);
|
||||
SynchronizeNewPrefabInstance(context, data, prefab, actor, e, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
class FLAXENGINE_API SceneObjectsFactory
|
||||
{
|
||||
public:
|
||||
struct NestedPrefabInstance
|
||||
{
|
||||
Prefab* Prefab;
|
||||
Guid RootObjectId;
|
||||
Dictionary<Guid, Guid> IdsMapping;
|
||||
};
|
||||
|
||||
struct PrefabInstance
|
||||
{
|
||||
int32 StatIndex;
|
||||
@@ -24,6 +31,8 @@ public:
|
||||
Prefab* Prefab;
|
||||
bool FixRootParent = false;
|
||||
Dictionary<Guid, Guid> IdsMapping;
|
||||
Array<NestedPrefabInstance> Nested;
|
||||
Dictionary<Guid, int32> ObjectToNested;
|
||||
};
|
||||
|
||||
struct Context
|
||||
@@ -136,5 +145,5 @@ public:
|
||||
|
||||
private:
|
||||
static void SynchronizeNewPrefabInstances(Context& context, PrefabSyncData& data, Prefab* prefab, Actor* actor, const Guid& actorPrefabObjectId, int32 i, const ISerializable::DeserializeStream& stream);
|
||||
static void SynchronizeNewPrefabInstance(Context& context, PrefabSyncData& data, Prefab* prefab, Actor* actor, const Guid& prefabObjectId);
|
||||
static void SynchronizeNewPrefabInstance(Context& context, PrefabSyncData& data, Prefab* prefab, Actor* actor, const Guid& prefabObjectId, const Guid& nestedInstanceId);
|
||||
};
|
||||
|
||||
@@ -462,7 +462,7 @@ void Cloth::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddPhysicsDebug<Cloth, &Cloth::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->AddPhysicsDebug(this);
|
||||
#endif
|
||||
#if WITH_CLOTH
|
||||
if (_cloth)
|
||||
@@ -481,7 +481,7 @@ void Cloth::OnDisable()
|
||||
PhysicsBackend::RemoveCloth(GetPhysicsScene()->GetPhysicsScene(), _cloth);
|
||||
#endif
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemovePhysicsDebug<Cloth, &Cloth::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->RemovePhysicsDebug(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Level/Actors/ModelInstanceActor.h"
|
||||
#include "IPhysicsDebug.h"
|
||||
|
||||
// Used internally to validate cloth data against invalid nan/inf values
|
||||
#define USE_CLOTH_SANITY_CHECKS 0
|
||||
@@ -12,6 +13,9 @@
|
||||
/// Physical simulation actor for cloth objects made of vertices that are simulated as cloth particles with physical properties, forces, and constraints to affect cloth behavior.
|
||||
/// </summary>
|
||||
API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Cloth\"), ActorToolbox(\"Physics\")") class FLAXENGINE_API Cloth : public Actor
|
||||
#if USE_EDITOR
|
||||
, public IPhysicsDebug
|
||||
#endif
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(Cloth);
|
||||
|
||||
@@ -364,9 +368,7 @@ protected:
|
||||
void OnPhysicsSceneChanged(PhysicsScene* previous) override;
|
||||
|
||||
private:
|
||||
#if USE_EDITOR
|
||||
void DrawPhysicsDebug(RenderView& view);
|
||||
#endif
|
||||
ImplementPhysicsDebug;
|
||||
bool CreateCloth();
|
||||
void DestroyCloth();
|
||||
void CalculateInvMasses(Array<float>& invMasses);
|
||||
|
||||
18
Source/Engine/Physics/Actors/IPhysicsDebug.h
Normal file
18
Source/Engine/Physics/Actors/IPhysicsDebug.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
|
||||
#if USE_EDITOR
|
||||
class FLAXENGINE_API IPhysicsDebug
|
||||
{
|
||||
public:
|
||||
virtual void DrawPhysicsDebug(struct RenderView& view)
|
||||
{
|
||||
}
|
||||
};
|
||||
#define ImplementPhysicsDebug void DrawPhysicsDebug(RenderView& view)
|
||||
#else
|
||||
#define ImplementPhysicsDebug
|
||||
#endif
|
||||
@@ -356,19 +356,19 @@ void WheeledVehicle::Setup()
|
||||
void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
|
||||
{
|
||||
// Wheels shapes
|
||||
for (const auto& data : _wheelsData)
|
||||
for (const auto& wheel : _wheels)
|
||||
{
|
||||
int32 wheelIndex = 0;
|
||||
for (; wheelIndex < _wheels.Count(); wheelIndex++)
|
||||
{
|
||||
if (_wheels[wheelIndex].Collider == data.Collider)
|
||||
break;
|
||||
}
|
||||
if (wheelIndex == _wheels.Count())
|
||||
break;
|
||||
const auto& wheel = _wheels[wheelIndex];
|
||||
if (wheel.Collider && wheel.Collider->GetParent() == this && !wheel.Collider->GetIsTrigger())
|
||||
{
|
||||
WheelData data = { wheel.Collider, wheel.Collider->GetLocalOrientation() };
|
||||
for (auto& e : _wheelsData)
|
||||
{
|
||||
if (e.Collider == data.Collider)
|
||||
{
|
||||
data = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const Vector3 currentPos = wheel.Collider->GetPosition();
|
||||
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
||||
const Quaternion wheelDebugOrientation = GetOrientation() * Quaternion::Euler(-data.State.RotationAngle, data.State.SteerAngle, 0) * Quaternion::Euler(90, 0, 90);
|
||||
@@ -387,25 +387,28 @@ void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
|
||||
void WheeledVehicle::OnDebugDrawSelected()
|
||||
{
|
||||
// Wheels shapes
|
||||
for (const auto& data : _wheelsData)
|
||||
for (const auto& wheel : _wheels)
|
||||
{
|
||||
int32 wheelIndex = 0;
|
||||
for (; wheelIndex < _wheels.Count(); wheelIndex++)
|
||||
{
|
||||
if (_wheels[wheelIndex].Collider == data.Collider)
|
||||
break;
|
||||
}
|
||||
if (wheelIndex == _wheels.Count())
|
||||
break;
|
||||
const auto& wheel = _wheels[wheelIndex];
|
||||
if (wheel.Collider && wheel.Collider->GetParent() == this && !wheel.Collider->GetIsTrigger())
|
||||
{
|
||||
WheelData data = { wheel.Collider, wheel.Collider->GetLocalOrientation() };
|
||||
for (auto& e : _wheelsData)
|
||||
{
|
||||
if (e.Collider == data.Collider)
|
||||
{
|
||||
data = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const Vector3 currentPos = wheel.Collider->GetPosition();
|
||||
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
||||
const Quaternion wheelDebugOrientation = GetOrientation() * Quaternion::Euler(-data.State.RotationAngle, data.State.SteerAngle, 0) * Quaternion::Euler(90, 0, 90);
|
||||
Transform actorPose = Transform::Identity, shapePose = Transform::Identity;
|
||||
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
|
||||
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
|
||||
Transform actorPose = GetTransform(), shapePose = wheel.Collider->GetLocalTransform();
|
||||
actorPose.Scale = Float3::One;
|
||||
if (_actor)
|
||||
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
|
||||
if (wheel.Collider->GetPhysicsShape())
|
||||
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(actorPose.LocalToWorld(shapePose.Translation), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
|
||||
@@ -561,14 +564,14 @@ void WheeledVehicle::BeginPlay(SceneBeginData* data)
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddPhysicsDebug<WheeledVehicle, &WheeledVehicle::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->AddPhysicsDebug(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WheeledVehicle::EndPlay()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemovePhysicsDebug<WheeledVehicle, &WheeledVehicle::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->RemovePhysicsDebug(this);
|
||||
#endif
|
||||
|
||||
#if WITH_VEHICLE
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
#include "Engine/Physics/Actors/RigidBody.h"
|
||||
#include "Engine/Physics/Colliders/Collider.h"
|
||||
#include "Engine/Scripting/ScriptingObjectReference.h"
|
||||
#include "IPhysicsDebug.h"
|
||||
|
||||
/// <summary>
|
||||
/// Representation of the car vehicle that uses wheels. Built on top of the RigidBody with collider representing its chassis shape and wheels.
|
||||
/// </summary>
|
||||
/// <seealso cref="RigidBody" />
|
||||
API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorToolbox(\"Physics\")") class FLAXENGINE_API WheeledVehicle : public RigidBody
|
||||
#if USE_EDITOR
|
||||
, public IPhysicsDebug
|
||||
#endif
|
||||
{
|
||||
friend class PhysicsBackend;
|
||||
friend struct ScenePhysX;
|
||||
@@ -644,13 +648,9 @@ public:
|
||||
/// </summary>
|
||||
API_FUNCTION() void Setup();
|
||||
|
||||
private:
|
||||
#if USE_EDITOR
|
||||
void DrawPhysicsDebug(RenderView& view);
|
||||
#endif
|
||||
|
||||
public:
|
||||
// [Vehicle]
|
||||
ImplementPhysicsDebug;
|
||||
#if USE_EDITOR
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
|
||||
@@ -58,9 +58,7 @@ public:
|
||||
|
||||
protected:
|
||||
// [Collider]
|
||||
ImplementPhysicsDebug;
|
||||
void UpdateBounds() override;
|
||||
void GetGeometry(CollisionShape& collision) override;
|
||||
#if USE_EDITOR
|
||||
void DrawPhysicsDebug(RenderView& view) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -62,9 +62,7 @@ public:
|
||||
|
||||
protected:
|
||||
// [Collider]
|
||||
ImplementPhysicsDebug;
|
||||
void UpdateBounds() override;
|
||||
void GetGeometry(CollisionShape& collision) override;
|
||||
#if USE_EDITOR
|
||||
void DrawPhysicsDebug(RenderView& view) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -267,13 +267,11 @@ public:
|
||||
|
||||
protected:
|
||||
// [PhysicsActor]
|
||||
ImplementPhysicsDebug;
|
||||
void UpdateGeometry() override;
|
||||
void GetGeometry(CollisionShape& collision) override;
|
||||
void BeginPlay(SceneBeginData* data) override;
|
||||
void EndPlay() override;
|
||||
#if USE_EDITOR
|
||||
void DrawPhysicsDebug(RenderView& view) override;
|
||||
#endif
|
||||
void OnActiveInTreeChanged() override;
|
||||
void OnEnable() override;
|
||||
void OnDisable() override;
|
||||
|
||||
@@ -145,7 +145,7 @@ void Collider::OnEnable()
|
||||
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger)
|
||||
GetScene()->Navigation.Actors.Add(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddPhysicsDebug<Collider, &Collider::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->AddPhysicsDebug(this);
|
||||
#endif
|
||||
|
||||
PhysicsColliderActor::OnEnable();
|
||||
@@ -158,7 +158,7 @@ void Collider::OnDisable()
|
||||
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger)
|
||||
GetScene()->Navigation.Actors.Remove(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemovePhysicsDebug<Collider, &Collider::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->RemovePhysicsDebug(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -284,14 +284,6 @@ void Collider::RemoveStaticActor()
|
||||
_staticActor = nullptr;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void Collider::DrawPhysicsDebug(RenderView& view)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Collider::BeginPlay(SceneBeginData* data)
|
||||
{
|
||||
// Check if has no shape created (it means no rigidbody requested it but also collider may be spawned at runtime)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
#include "Engine/Content/JsonAssetReference.h"
|
||||
#include "Engine/Physics/Actors/PhysicsColliderActor.h"
|
||||
#include "Engine/Physics/Actors/IPhysicsDebug.h"
|
||||
|
||||
struct RayCastHit;
|
||||
class RigidBody;
|
||||
@@ -16,6 +17,9 @@ class RigidBody;
|
||||
/// <seealso cref="Actor" />
|
||||
/// <seealso cref="PhysicsColliderActor" />
|
||||
API_CLASS(Abstract) class FLAXENGINE_API Collider : public PhysicsColliderActor, protected IAssetReference
|
||||
#if USE_EDITOR
|
||||
, public IPhysicsDebug
|
||||
#endif
|
||||
{
|
||||
API_AUTO_SERIALIZATION();
|
||||
DECLARE_SCENE_OBJECT_ABSTRACT(Collider);
|
||||
@@ -162,10 +166,6 @@ public:
|
||||
void ClosestPoint(const Vector3& point, Vector3& result) const final;
|
||||
bool ContainsPoint(const Vector3& point) const final;
|
||||
|
||||
#if USE_EDITOR
|
||||
virtual void DrawPhysicsDebug(RenderView& view);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// [PhysicsColliderActor]
|
||||
void OnEnable() override;
|
||||
|
||||
@@ -37,9 +37,7 @@ public:
|
||||
|
||||
protected:
|
||||
// [Collider]
|
||||
#if USE_EDITOR
|
||||
void DrawPhysicsDebug(RenderView& view) override;
|
||||
#endif
|
||||
ImplementPhysicsDebug;
|
||||
void UpdateBounds() override;
|
||||
void GetGeometry(CollisionShape& collision) override;
|
||||
void OnAssetChanged(Asset* asset, void* caller) override;
|
||||
|
||||
@@ -42,9 +42,7 @@ public:
|
||||
|
||||
protected:
|
||||
// [Collider]
|
||||
#if USE_EDITOR
|
||||
void DrawPhysicsDebug(RenderView& view) override;
|
||||
#endif
|
||||
ImplementPhysicsDebug;
|
||||
void UpdateBounds() override;
|
||||
void GetGeometry(CollisionShape& collision) override;
|
||||
};
|
||||
|
||||
@@ -67,9 +67,7 @@ public:
|
||||
|
||||
protected:
|
||||
// [Collider]
|
||||
#if USE_EDITOR
|
||||
void DrawPhysicsDebug(RenderView& view) override;
|
||||
#endif
|
||||
ImplementPhysicsDebug;
|
||||
void UpdateBounds() override;
|
||||
void GetGeometry(CollisionShape& collision) override;
|
||||
};
|
||||
|
||||
@@ -298,7 +298,7 @@ void Joint::EndPlay()
|
||||
|
||||
void Joint::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddPhysicsDebug<Joint, &Joint::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->AddPhysicsDebug(this);
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
@@ -306,7 +306,7 @@ void Joint::OnEnable()
|
||||
|
||||
void Joint::OnDisable()
|
||||
{
|
||||
GetSceneRendering()->RemovePhysicsDebug<Joint, &Joint::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->RemovePhysicsDebug(this);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Physics/Types.h"
|
||||
#include "Engine/Scripting/ScriptingObjectReference.h"
|
||||
#include "Engine/Physics/Actors/IPhysicsDebug.h"
|
||||
|
||||
class IPhysicsActor;
|
||||
|
||||
@@ -17,6 +18,9 @@ class IPhysicsActor;
|
||||
/// </remarks>
|
||||
/// <seealso cref="Actor" />
|
||||
API_CLASS(Abstract) class FLAXENGINE_API Joint : public Actor
|
||||
#if USE_EDITOR
|
||||
, public IPhysicsDebug
|
||||
#endif
|
||||
{
|
||||
DECLARE_SCENE_OBJECT_ABSTRACT(Joint);
|
||||
protected:
|
||||
@@ -174,9 +178,7 @@ protected:
|
||||
Vector3 GetTargetPosition() const;
|
||||
Quaternion GetTargetOrientation() const;
|
||||
virtual void* CreateJoint(const struct PhysicsJointDesc& desc) = 0;
|
||||
#if USE_EDITOR
|
||||
virtual void DrawPhysicsDebug(RenderView& view);
|
||||
#endif
|
||||
ImplementPhysicsDebug;
|
||||
|
||||
private:
|
||||
void Delete();
|
||||
|
||||
@@ -1366,10 +1366,11 @@ RETRY_ATLAS_SETUP:
|
||||
tile.LinkedRectTile = nullptr;
|
||||
auto& linkedTile = linkedAtlasLight->Tiles[tileIndex];
|
||||
|
||||
// Check if both lights use the same projections
|
||||
if (tile.WorldToShadow == linkedTile.WorldToShadow && linkedTile.RectTile)
|
||||
// Link tile and use its projection
|
||||
if (linkedTile.RectTile)
|
||||
{
|
||||
tile.LinkedRectTile = linkedTile.RectTile;
|
||||
tile.WorldToShadow = linkedTile.WorldToShadow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,14 +790,14 @@ void Serialization::Deserialize(ISerializable::DeserializeStream& stream, Matrix
|
||||
DESERIALIZE_HELPER(stream, "M44", v.M44, 0);
|
||||
}
|
||||
|
||||
bool Serialization::ShouldSerialize(const SceneObject* v, const SceneObject* other)
|
||||
bool Serialization::ShouldSerializeRef(const SceneObject* v, const SceneObject* other)
|
||||
{
|
||||
bool result = v != other;
|
||||
if (result && v && other && v->HasPrefabLink() && other->HasPrefabLink())
|
||||
{
|
||||
// Special case when saving reference to prefab object and the objects are different but the point to the same prefab object
|
||||
// In that case, skip saving reference as it's defined in prefab (will be populated via IdsMapping during deserialization)
|
||||
result &= v->GetPrefabObjectID() != other->GetPrefabObjectID();
|
||||
result = v->GetPrefabObjectID() != other->GetPrefabObjectID();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -448,15 +448,20 @@ namespace Serialization
|
||||
|
||||
// Scripting Object
|
||||
|
||||
FLAXENGINE_API bool ShouldSerialize(const SceneObject* v, const SceneObject* other);
|
||||
inline bool ShouldSerializeRef(const ScriptingObject* v, const ScriptingObject* other)
|
||||
{
|
||||
return v != other;
|
||||
}
|
||||
|
||||
FLAXENGINE_API bool ShouldSerializeRef(const SceneObject* v, const SceneObject* other);
|
||||
|
||||
template<typename T>
|
||||
inline typename TEnableIf<TIsBaseOf<ScriptingObject, T>::Value, bool>::Type ShouldSerialize(const T*& v, const void* otherObj)
|
||||
inline typename TEnableIf<TAnd<TIsBaseOf<ScriptingObject, T>, TNot<TIsBaseOf<SceneObject, T>>>::Value, bool>::Type ShouldSerialize(const T* v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || v != *(const T**)otherObj;
|
||||
}
|
||||
template<typename T>
|
||||
inline typename TEnableIf<TIsBaseOf<ScriptingObject, T>::Value>::Type Serialize(ISerializable::SerializeStream& stream, const T*& v, const void* otherObj)
|
||||
inline typename TEnableIf<TIsBaseOf<ScriptingObject, T>::Value>::Type Serialize(ISerializable::SerializeStream& stream, const T* v, const void* otherObj)
|
||||
{
|
||||
stream.Guid(v ? v->GetID() : Guid::Empty);
|
||||
}
|
||||
@@ -470,9 +475,9 @@ namespace Serialization
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename TEnableIf<TIsBaseOf<SceneObject, T>::Value, bool>::Type ShouldSerialize(const T*& v, const void* otherObj)
|
||||
inline typename TEnableIf<TIsBaseOf<SceneObject, T>::Value, bool>::Type ShouldSerialize(const T* v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || ShouldSerialize((const SceneObject*)v, *(const SceneObject**)otherObj);
|
||||
return !otherObj || ShouldSerializeRef((const SceneObject*)v, *(const SceneObject**)otherObj);
|
||||
}
|
||||
|
||||
// Scripting Object Reference
|
||||
@@ -480,7 +485,7 @@ namespace Serialization
|
||||
template<typename T>
|
||||
inline bool ShouldSerialize(const ScriptingObjectReference<T>& v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || ShouldSerialize(v.Get(), ((ScriptingObjectReference<T>*)otherObj)->Get());
|
||||
return !otherObj || ShouldSerializeRef(v.Get(), ((ScriptingObjectReference<T>*)otherObj)->Get());
|
||||
}
|
||||
template<typename T>
|
||||
inline void Serialize(ISerializable::SerializeStream& stream, const ScriptingObjectReference<T>& v, const void* otherObj)
|
||||
@@ -501,7 +506,7 @@ namespace Serialization
|
||||
template<typename T>
|
||||
inline bool ShouldSerialize(const SoftObjectReference<T>& v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || ShouldSerialize(v.Get(), ((SoftObjectReference<T>*)otherObj)->Get());
|
||||
return !otherObj || ShouldSerializeRef(v.Get(), ((SoftObjectReference<T>*)otherObj)->Get());
|
||||
}
|
||||
template<typename T>
|
||||
inline void Serialize(ISerializable::SerializeStream& stream, const SoftObjectReference<T>& v, const void* otherObj)
|
||||
|
||||
@@ -80,7 +80,7 @@ class ISerializeModifier;
|
||||
// Explicit auto-cast for object pointer
|
||||
|
||||
#define SERIALIZE_OBJ(name) \
|
||||
if (Serialization::ShouldSerialize((const ScriptingObject*&)name, other ? &other->name : nullptr)) \
|
||||
if (Serialization::ShouldSerialize(name, decltype(name)(other ? &other->name : nullptr))) \
|
||||
{ \
|
||||
stream.JKEY(#name); \
|
||||
Serialization::Serialize(stream, (const ScriptingObject*&)name, other ? &other->name : nullptr); \
|
||||
|
||||
@@ -852,7 +852,7 @@ void Terrain::OnEnable()
|
||||
GetScene()->Navigation.Actors.Add(this);
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
GetSceneRendering()->AddPhysicsDebug<Terrain, &Terrain::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->AddPhysicsDebug(this);
|
||||
#endif
|
||||
void* scene = GetPhysicsScene()->GetPhysicsScene();
|
||||
for (int32 i = 0; i < _patches.Count(); i++)
|
||||
@@ -873,7 +873,7 @@ void Terrain::OnDisable()
|
||||
GetScene()->Navigation.Actors.Remove(this);
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
GetSceneRendering()->RemovePhysicsDebug<Terrain, &Terrain::DrawPhysicsDebug>(this);
|
||||
GetSceneRendering()->RemovePhysicsDebug(this);
|
||||
#endif
|
||||
void* scene = GetPhysicsScene()->GetPhysicsScene();
|
||||
for (int32 i = 0; i < _patches.Count(); i++)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Content/JsonAssetReference.h"
|
||||
#include "Engine/Content/Assets/MaterialBase.h"
|
||||
#include "Engine/Physics/Actors/PhysicsColliderActor.h"
|
||||
#include "Engine/Physics/Actors/IPhysicsDebug.h"
|
||||
|
||||
class Terrain;
|
||||
class TerrainChunk;
|
||||
@@ -38,6 +39,9 @@ struct RenderView;
|
||||
/// <seealso cref="Actor" />
|
||||
/// <seealso cref="PhysicsColliderActor" />
|
||||
API_CLASS(Sealed) class FLAXENGINE_API Terrain : public PhysicsColliderActor
|
||||
#if USE_EDITOR
|
||||
, public IPhysicsDebug
|
||||
#endif
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(Terrain);
|
||||
friend Terrain;
|
||||
@@ -441,9 +445,7 @@ public:
|
||||
API_FUNCTION() void DrawChunk(API_PARAM(Ref) const RenderContext& renderContext, API_PARAM(Ref) const Int2& patchCoord, API_PARAM(Ref) const Int2& chunkCoord, MaterialBase* material, int32 lodIndex = 0) const;
|
||||
|
||||
private:
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
void DrawPhysicsDebug(RenderView& view);
|
||||
#endif
|
||||
ImplementPhysicsDebug;
|
||||
bool DrawSetup(RenderContext& renderContext);
|
||||
void DrawImpl(RenderContext& renderContext, HashSet<TerrainChunk*, class RendererAllocation>& drawnChunks);
|
||||
|
||||
|
||||
@@ -687,6 +687,171 @@ TEST_CASE("Prefabs")
|
||||
instanceA->DeleteObject();
|
||||
instanceB->DeleteObject();
|
||||
instanceC->DeleteObject();
|
||||
}
|
||||
SECTION("Test Loading Nested Prefab With Multiple Instances of Nested Prefab")
|
||||
{
|
||||
// https://github.com/FlaxEngine/FlaxEngine/issues/3255
|
||||
|
||||
// Create Prefab C with basic cross-object references
|
||||
AssetReference<Prefab> prefabC = Content::CreateVirtualAsset<Prefab>();
|
||||
REQUIRE(prefabC);
|
||||
Guid id;
|
||||
Guid::Parse("cccbe4b0416be0777a6ce59e8788b10f", id);
|
||||
prefabC->ChangeID(id);
|
||||
auto prefabCInit = prefabC->Init(Prefab::TypeName,
|
||||
"["
|
||||
"{"
|
||||
"\"ID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"TypeName\": \"FlaxEngine.ExponentialHeightFog\","
|
||||
"\"Name\": \"Prefab C.Root\","
|
||||
"\"DirectionalInscatteringLight\": \"bbb6b9644492fbca1a6ab0a7904a557e\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"bbb6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"TypeName\": \"FlaxEngine.DirectionalLight\","
|
||||
"\"ParentID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"Name\": \"Prefab C.Light\""
|
||||
"}"
|
||||
"]");
|
||||
REQUIRE(!prefabCInit);
|
||||
|
||||
// Create Prefab B with two nested Prefab C attached to the root
|
||||
AssetReference<Prefab> prefabB = Content::CreateVirtualAsset<Prefab>();
|
||||
REQUIRE(prefabB);
|
||||
SCOPE_EXIT{ Content::DeleteAsset(prefabB); };
|
||||
Guid::Parse("bbb744714f746e31855f41815612d14b", id);
|
||||
prefabB->ChangeID(id);
|
||||
auto prefabBInit = prefabB->Init(Prefab::TypeName,
|
||||
"["
|
||||
"{"
|
||||
"\"ID\": \"244274a04cc60d56a2f024bfeef5772d\","
|
||||
"\"TypeName\": \"FlaxEngine.SpotLight\","
|
||||
"\"Name\": \"Prefab B.Root\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"1111f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"cccbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"2221f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"cccbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"bbb6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"ParentID\": \"1111f1094f430733333f8280e78dfcc3\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"3331f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"cccbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"4441f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"cccbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"bbb6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"ParentID\": \"3331f1094f430733333f8280e78dfcc3\""
|
||||
"}"
|
||||
"]");
|
||||
REQUIRE(!prefabBInit);
|
||||
|
||||
// Create Prefab A as variant of Prefab B (no local changes, just object remapped)
|
||||
AssetReference<Prefab> prefabA = Content::CreateVirtualAsset<Prefab>();
|
||||
REQUIRE(prefabA);
|
||||
SCOPE_EXIT{ Content::DeleteAsset(prefabA); };
|
||||
Guid::Parse("aaa744714f746e31855f41815612d14b", id);
|
||||
prefabA->ChangeID(id);
|
||||
auto prefabAInit = prefabA->Init(Prefab::TypeName,
|
||||
"["
|
||||
"{"
|
||||
"\"ID\": \"123274a04cc60d56a2f024bfeef5772d\","
|
||||
"\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
|
||||
"\"PrefabObjectID\": \"244274a04cc60d56a2f024bfeef5772d\","
|
||||
"\"Name\": \"Prefab A.Root\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"1211f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
|
||||
"\"PrefabObjectID\": \"1111f1094f430733333f8280e78dfcc3\","
|
||||
"\"ParentID\": \"123274a04cc60d56a2f024bfeef5772d\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"4221f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
|
||||
"\"PrefabObjectID\": \"2221f1094f430733333f8280e78dfcc3\","
|
||||
"\"ParentID\": \"1211f1094f430733333f8280e78dfcc3\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"3131f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
|
||||
"\"PrefabObjectID\": \"3331f1094f430733333f8280e78dfcc3\","
|
||||
"\"ParentID\": \"123274a04cc60d56a2f024bfeef5772d\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"5441f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"bbb744714f746e31855f41815612d14b\","
|
||||
"\"PrefabObjectID\": \"4441f1094f430733333f8280e78dfcc3\","
|
||||
"\"ParentID\": \"3131f1094f430733333f8280e78dfcc3\""
|
||||
"}"
|
||||
"]");
|
||||
REQUIRE(!prefabAInit);
|
||||
|
||||
// Spawn test instances of both prefabs
|
||||
ScriptingObjectReference<Actor> instanceA = PrefabManager::SpawnPrefab(prefabA);
|
||||
ScriptingObjectReference<Actor> instanceB = PrefabManager::SpawnPrefab(prefabB);
|
||||
ScriptingObjectReference<Actor> instanceC = PrefabManager::SpawnPrefab(prefabC);
|
||||
|
||||
// Check state of objects
|
||||
REQUIRE(instanceC);
|
||||
REQUIRE(instanceC->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceC->Children.Count() == 1);
|
||||
CHECK(instanceC.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceC->Children[0]);
|
||||
REQUIRE(instanceB);
|
||||
REQUIRE(instanceB->Children.Count() == 2);
|
||||
ScriptingObjectReference<Actor> instanceB1 = instanceB->Children[0];
|
||||
ScriptingObjectReference<Actor> instanceB2 = instanceB->Children[1];
|
||||
REQUIRE(instanceB1->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceB1->Children.Count() == 1);
|
||||
CHECK(instanceB1.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceB1->Children[0]);
|
||||
REQUIRE(instanceB2->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceB2->Children.Count() == 1);
|
||||
CHECK(instanceB2.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceB2->Children[0]);
|
||||
REQUIRE(instanceA);
|
||||
REQUIRE(instanceA->Children.Count() == 2);
|
||||
ScriptingObjectReference<Actor> instanceA1 = instanceA->Children[0];
|
||||
ScriptingObjectReference<Actor> instanceA2 = instanceA->Children[1];
|
||||
REQUIRE(instanceA1->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceA1->Children.Count() == 1);
|
||||
CHECK(instanceA1.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA1->Children[0]);
|
||||
REQUIRE(instanceA2->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceA1->Children.Count() == 1);
|
||||
CHECK(instanceA2.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA2->Children[0]);
|
||||
|
||||
// Add instance of Prefab C to Prefab B
|
||||
instanceC->SetName(StringView(TEXT("New")));
|
||||
instanceC->SetParent(instanceB);
|
||||
bool applyResult = PrefabManager::ApplyAll(instanceB);
|
||||
REQUIRE(!applyResult);
|
||||
|
||||
// Check if Prefab A reflects that change
|
||||
REQUIRE(instanceA);
|
||||
REQUIRE(instanceA->Children.Count() == 3);
|
||||
instanceA1 = instanceA->Children[0];
|
||||
instanceA2 = instanceA->Children[1];
|
||||
ScriptingObjectReference<Actor> instanceA3 = instanceA->Children[2];
|
||||
REQUIRE(instanceA1->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceA1->Children.Count() == 1);
|
||||
CHECK(instanceA1.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA1->Children[0]);
|
||||
REQUIRE(instanceA2->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceA2->Children.Count() == 1);
|
||||
CHECK(instanceA2.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA2->Children[0]);
|
||||
REQUIRE(instanceA3->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceA3->Children.Count() == 1);
|
||||
CHECK(instanceA3.As<ExponentialHeightFog>()->DirectionalInscatteringLight == instanceA3->Children[0]);
|
||||
|
||||
// Cleanup
|
||||
instanceA->DeleteObject();
|
||||
instanceB->DeleteObject();
|
||||
instanceC->DeleteObject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ void TextRender::UpdateLayout()
|
||||
|
||||
// Pick a font (remove DPI text scale as the text is being placed in the world)
|
||||
auto font = Font->CreateFont(_size);
|
||||
float scale = 1.0f / FontManager::FontScale;
|
||||
float scale = _layoutOptions.Scale / FontManager::FontScale;
|
||||
|
||||
// Prepare
|
||||
FontTextureAtlas* fontAtlas = nullptr;
|
||||
|
||||
@@ -122,10 +122,6 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
|
||||
// Calculate shadow
|
||||
ShadowSample shadow = GetShadow(lightData, gBuffer, shadowMask);
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Directional shadowing
|
||||
shadow.SurfaceShadow *= NoL;
|
||||
#endif
|
||||
|
||||
// Calculate attenuation
|
||||
if (isRadial)
|
||||
@@ -139,6 +135,11 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
shadow.TransmissionShadow *= attenuation;
|
||||
}
|
||||
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Directional shadowing
|
||||
shadow.SurfaceShadow *= NoL;
|
||||
#endif
|
||||
|
||||
BRANCH
|
||||
if (shadow.SurfaceShadow + shadow.TransmissionShadow > 0)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Flax.Build.Bindings
|
||||
public FileInfo File;
|
||||
public Tokenizer Tokenizer;
|
||||
public ApiTypeInfo ScopeInfo;
|
||||
public NativeCpp.BuildOptions ModuleOptions;
|
||||
public AccessLevel CurrentAccessLevel;
|
||||
public Stack<ApiTypeInfo> ScopeTypeStack;
|
||||
public Stack<AccessLevel> ScopeAccessStack;
|
||||
@@ -534,6 +535,12 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
if (token.Type == TokenType.LeftCurlyBrace)
|
||||
break;
|
||||
if (token.Type == TokenType.Preprocessor)
|
||||
{
|
||||
OnPreProcessorToken(ref context, ref token);
|
||||
while (token.Type == TokenType.Newline || token.Value == "endif")
|
||||
token = context.Tokenizer.NextToken();
|
||||
}
|
||||
if (token.Type == TokenType.Colon)
|
||||
{
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Colon);
|
||||
|
||||
@@ -245,6 +245,7 @@ namespace Flax.Build.Bindings
|
||||
File = fileInfo,
|
||||
Tokenizer = tokenizer,
|
||||
ScopeInfo = null,
|
||||
ModuleOptions = moduleOptions,
|
||||
CurrentAccessLevel = AccessLevel.Public,
|
||||
ScopeTypeStack = new Stack<ApiTypeInfo>(),
|
||||
ScopeAccessStack = new Stack<AccessLevel>(),
|
||||
@@ -380,110 +381,7 @@ namespace Flax.Build.Bindings
|
||||
// Handle preprocessor blocks
|
||||
if (token.Type == TokenType.Preprocessor)
|
||||
{
|
||||
token = tokenizer.NextToken();
|
||||
switch (token.Value)
|
||||
{
|
||||
case "define":
|
||||
{
|
||||
token = tokenizer.NextToken();
|
||||
var name = token.Value;
|
||||
var value = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
value += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
value = value.Trim();
|
||||
context.PreprocessorDefines[name] = value;
|
||||
break;
|
||||
}
|
||||
case "if":
|
||||
case "elif":
|
||||
{
|
||||
// Parse condition
|
||||
var condition = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
var tokenValue = token.Value.Trim();
|
||||
if (tokenValue.Length == 0)
|
||||
{
|
||||
token = tokenizer.NextToken(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Very simple defines processing
|
||||
var negate = tokenValue[0] == '!';
|
||||
if (negate)
|
||||
tokenValue = tokenValue.Substring(1);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, context.PreprocessorDefines);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PublicDefinitions);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PrivateDefinitions);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.CompileEnv.PreprocessorDefinitions);
|
||||
tokenValue = tokenValue.Replace("false", "0");
|
||||
tokenValue = tokenValue.Replace("true", "1");
|
||||
tokenValue = tokenValue.Replace("||", "|");
|
||||
if (tokenValue.Length != 0 && tokenValue != "1" && tokenValue != "0" && tokenValue != "|")
|
||||
tokenValue = "0";
|
||||
if (negate)
|
||||
tokenValue = tokenValue == "1" ? "0" : "1";
|
||||
|
||||
condition += tokenValue;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
|
||||
// Filter condition
|
||||
bool modified;
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
if (condition.Contains("1|1"))
|
||||
{
|
||||
condition = condition.Replace("1|1", "1");
|
||||
modified = true;
|
||||
}
|
||||
if (condition.Contains("1|0"))
|
||||
{
|
||||
condition = condition.Replace("1|0", "1");
|
||||
modified = true;
|
||||
}
|
||||
if (condition.Contains("0|1"))
|
||||
{
|
||||
condition = condition.Replace("0|1", "1");
|
||||
modified = true;
|
||||
}
|
||||
} while (modified);
|
||||
|
||||
// Skip chunk of code of condition fails
|
||||
if (condition != "1")
|
||||
{
|
||||
ParsePreprocessorIf(fileInfo, tokenizer, ref token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "ifdef":
|
||||
{
|
||||
// Parse condition
|
||||
var define = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
define += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
|
||||
// Check condition
|
||||
define = define.Trim();
|
||||
if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define))
|
||||
{
|
||||
ParsePreprocessorIf(fileInfo, tokenizer, ref token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
OnPreProcessorToken(ref context, ref token);
|
||||
}
|
||||
|
||||
// Scope tracking
|
||||
@@ -514,6 +412,120 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnPreProcessorToken(ref ParsingContext context, ref Token token)
|
||||
{
|
||||
var tokenizer = context.Tokenizer;
|
||||
token = tokenizer.NextToken();
|
||||
switch (token.Value)
|
||||
{
|
||||
case "define":
|
||||
{
|
||||
token = tokenizer.NextToken();
|
||||
var name = token.Value;
|
||||
var value = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
value += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
value = value.Trim();
|
||||
context.PreprocessorDefines[name] = value;
|
||||
break;
|
||||
}
|
||||
case "if":
|
||||
case "elif":
|
||||
{
|
||||
// Parse condition
|
||||
var condition = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
var tokenValue = token.Value.Trim();
|
||||
if (tokenValue.Length == 0)
|
||||
{
|
||||
token = tokenizer.NextToken(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Very simple defines processing
|
||||
var negate = tokenValue[0] == '!';
|
||||
if (negate)
|
||||
tokenValue = tokenValue.Substring(1);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, context.PreprocessorDefines);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, context.ModuleOptions.PublicDefinitions);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, context.ModuleOptions.PrivateDefinitions);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, context.ModuleOptions.CompileEnv.PreprocessorDefinitions);
|
||||
tokenValue = tokenValue.Replace("false", "0");
|
||||
tokenValue = tokenValue.Replace("true", "1");
|
||||
tokenValue = tokenValue.Replace("||", "|");
|
||||
if (tokenValue.Length != 0 && tokenValue != "1" && tokenValue != "0" && tokenValue != "|")
|
||||
tokenValue = "0";
|
||||
if (negate)
|
||||
tokenValue = tokenValue == "1" ? "0" : "1";
|
||||
|
||||
condition += tokenValue;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
|
||||
// Filter condition
|
||||
bool modified;
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
if (condition.Contains("1|1"))
|
||||
{
|
||||
condition = condition.Replace("1|1", "1");
|
||||
modified = true;
|
||||
}
|
||||
if (condition.Contains("1|0"))
|
||||
{
|
||||
condition = condition.Replace("1|0", "1");
|
||||
modified = true;
|
||||
}
|
||||
if (condition.Contains("0|1"))
|
||||
{
|
||||
condition = condition.Replace("0|1", "1");
|
||||
modified = true;
|
||||
}
|
||||
} while (modified);
|
||||
|
||||
// Skip chunk of code of condition fails
|
||||
if (condition != "1")
|
||||
{
|
||||
ParsePreprocessorIf(context.File, tokenizer, ref token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "ifdef":
|
||||
{
|
||||
// Parse condition
|
||||
var define = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
define += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
|
||||
// Check condition
|
||||
define = define.Trim();
|
||||
if (!context.PreprocessorDefines.ContainsKey(define) && !context.ModuleOptions.CompileEnv.PreprocessorDefinitions.Contains(define))
|
||||
{
|
||||
ParsePreprocessorIf(context.File, tokenizer, ref token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "endif":
|
||||
{
|
||||
token = tokenizer.NextToken(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReplacePreProcessorDefines(string text, IEnumerable<string> defines)
|
||||
{
|
||||
foreach (var define in defines)
|
||||
|
||||
Reference in New Issue
Block a user