Merge remote-tracking branch 'origin/master' into 1.10
This commit is contained in:
BIN
Content/Shaders/Editor/Grid.flax
(Stored with Git LFS)
BIN
Content/Shaders/Editor/Grid.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Quad.flax
(Stored with Git LFS)
BIN
Content/Shaders/Quad.flax
(Stored with Git LFS)
Binary file not shown.
@@ -92,7 +92,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (IsLinearTangentMode(spline, index) || IsSmoothInTangentMode(spline, index) || IsSmoothOutTangentMode(spline, index))
|
||||
{
|
||||
SetPointSmooth(spline, index);
|
||||
SetPointSmooth(Editor, spline, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (!IsAlignedTangentMode(spline, index))
|
||||
{
|
||||
SetPointSmooth(spline, index);
|
||||
SetPointSmooth(Editor, spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <inheritdoc/>
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
SetTangentSmoothIn(spline, index);
|
||||
SetTangentSmoothIn(Editor, spline, index);
|
||||
Editor.SetSelectTangentIn(spline, index);
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <inheritdoc/>
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
SetTangentSmoothOut(spline, index);
|
||||
SetTangentSmoothOut(Editor, spline, index);
|
||||
Editor.SetSelectTangentOut(spline, index);
|
||||
}
|
||||
}
|
||||
@@ -756,14 +756,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
spline.UpdateSpline();
|
||||
}
|
||||
|
||||
private static void SetTangentSmoothIn(Spline spline, int index)
|
||||
private static void SetTangentSmoothIn(SplineEditor editor, Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
|
||||
// Auto smooth tangent if's linear
|
||||
if (keyframe.TangentIn.Translation.Length == 0)
|
||||
{
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f);
|
||||
var cameraTransform = editor.Presenter.Owner.PresenterViewport.ViewTransform.Translation;
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f, cameraTransform);
|
||||
var previousKeyframe = spline.GetSplineKeyframe(index - 1);
|
||||
var tangentDirection = keyframe.Value.WorldToLocalVector(previousKeyframe.Value.Translation - keyframe.Value.Translation);
|
||||
tangentDirection = tangentDirection.Normalized * smoothRange;
|
||||
@@ -775,14 +776,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
spline.UpdateSpline();
|
||||
}
|
||||
|
||||
private static void SetTangentSmoothOut(Spline spline, int index)
|
||||
private static void SetTangentSmoothOut(SplineEditor editor, Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
|
||||
// Auto smooth tangent if's linear
|
||||
if (keyframe.TangentOut.Translation.Length == 0)
|
||||
{
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f);
|
||||
var cameraTransform = editor.Presenter.Owner.PresenterViewport.ViewTransform.Translation;
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplineTangent(index, false).Translation, 10f, cameraTransform);
|
||||
var nextKeyframe = spline.GetSplineKeyframe(index + 1);
|
||||
var tangentDirection = keyframe.Value.WorldToLocalVector(nextKeyframe.Value.Translation - keyframe.Value.Translation);
|
||||
tangentDirection = tangentDirection.Normalized * smoothRange;
|
||||
@@ -795,7 +797,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
spline.UpdateSpline();
|
||||
}
|
||||
|
||||
private static void SetPointSmooth(Spline spline, int index)
|
||||
private static void SetPointSmooth(SplineEditor editor, Spline spline, int index)
|
||||
{
|
||||
var keyframe = spline.GetSplineKeyframe(index);
|
||||
var tangentInSize = keyframe.TangentIn.Translation.Length;
|
||||
@@ -803,7 +805,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
var isLastKeyframe = index >= spline.SplinePointsCount - 1;
|
||||
var isFirstKeyframe = index <= 0;
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplinePoint(index), 10f);
|
||||
var cameraTransform = editor.Presenter.Owner.PresenterViewport.ViewTransform.Translation;
|
||||
var smoothRange = SplineNode.NodeSizeByDistance(spline.GetSplinePoint(index), 10f, cameraTransform);
|
||||
|
||||
// Force smooth it's linear point
|
||||
if (tangentInSize == 0f)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors;
|
||||
|
||||
/// <summary>
|
||||
/// The reference picker control used for UIControls using ControlReference.
|
||||
/// </summary>
|
||||
internal class UIControlRefPickerControl : FlaxObjectRefPickerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the control to pick.
|
||||
/// </summary>
|
||||
public Type ControlType;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsValid(Object obj)
|
||||
{
|
||||
return obj == null || (obj is UIControl control && control.Control.GetType() == ControlType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ControlReferenceEditor class.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ControlReference<>)), DefaultEditor]
|
||||
internal class ControlReferenceEditor : CustomEditor
|
||||
{
|
||||
private CustomElement<UIControlRefPickerControl> _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
if (!HasDifferentTypes)
|
||||
{
|
||||
_element = layout.Custom<UIControlRefPickerControl>();
|
||||
if (ValuesTypes == null || ValuesTypes[0] == null)
|
||||
{
|
||||
Editor.LogWarning("ControlReference needs to be assigned in code.");
|
||||
return;
|
||||
}
|
||||
Type genType = ValuesTypes[0].GetGenericArguments()[0];
|
||||
if (typeof(Control).IsAssignableFrom(genType))
|
||||
{
|
||||
_element.CustomControl.PresenterContext = Presenter.Owner;
|
||||
_element.CustomControl.ControlType = genType;
|
||||
_element.CustomControl.Type = new ScriptType(typeof(UIControl));
|
||||
}
|
||||
_element.CustomControl.ValueChanged += () =>
|
||||
{
|
||||
Type genericType = ValuesTypes[0].GetGenericArguments()[0];
|
||||
if (typeof(Control).IsAssignableFrom(genericType))
|
||||
{
|
||||
Type t = typeof(ControlReference<>);
|
||||
Type tw = t.MakeGenericType(new Type[] { genericType });
|
||||
var instance = Activator.CreateInstance(tw);
|
||||
((IControlReference)instance).UIControl = (UIControl)_element.CustomControl.Value;
|
||||
SetValue(instance);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (!HasDifferentValues)
|
||||
{
|
||||
if (Values[0] is IControlReference cr)
|
||||
_element.CustomControl.Value = cr.UIControl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public IPresenterOwner PresenterContext;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the allowed objects type (given type and all sub classes). Must be <see cref="Object"/> type of any subclass.
|
||||
/// Gets or sets the allowed objects type (given type and all subclasses). Must be <see cref="Object"/> type of any subclass.
|
||||
/// </summary>
|
||||
public ScriptType Type
|
||||
{
|
||||
@@ -61,7 +61,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
throw new ArgumentException(string.Format("Invalid type for FlaxObjectRefEditor. Input type: {0}", value != ScriptType.Null ? value.TypeName : "null"));
|
||||
|
||||
_type = value;
|
||||
_supportsPickDropDown = new ScriptType(typeof(Actor)).IsAssignableFrom(value) || new ScriptType(typeof(Script)).IsAssignableFrom(value);
|
||||
_supportsPickDropDown = new ScriptType(typeof(Actor)).IsAssignableFrom(value) ||
|
||||
new ScriptType(typeof(Script)).IsAssignableFrom(value);
|
||||
|
||||
// Deselect value if it's not valid now
|
||||
if (!IsValid(_value))
|
||||
@@ -80,7 +81,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
if (_value == value)
|
||||
return;
|
||||
if (!IsValid(value))
|
||||
throw new ArgumentException("Invalid object type.");
|
||||
value = null;
|
||||
|
||||
// Special case for missing objects (eg. referenced actor in script that is deleted in editor)
|
||||
if (value != null && (Object.GetUnmanagedPtr(value) == IntPtr.Zero || value.ID == Guid.Empty))
|
||||
@@ -91,27 +92,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
// Get name to display
|
||||
if (_value is Script script)
|
||||
{
|
||||
_valueName = script.Actor ? $"{type.Name} ({script.Actor.Name})" : type.Name;
|
||||
}
|
||||
else if (_value != null)
|
||||
{
|
||||
_valueName = _value.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
_valueName = string.Empty;
|
||||
}
|
||||
|
||||
// Update tooltip
|
||||
if (_value is SceneObject sceneObject)
|
||||
{
|
||||
TooltipText = Utilities.Utils.GetTooltip(sceneObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
TooltipText = string.Empty;
|
||||
}
|
||||
|
||||
OnValueChanged();
|
||||
}
|
||||
@@ -150,7 +141,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_type = ScriptType.Object;
|
||||
}
|
||||
|
||||
private bool IsValid(Object obj)
|
||||
/// <summary>
|
||||
/// Object validation check routine.
|
||||
/// </summary>
|
||||
/// <param name="obj">Input object to check.</param>
|
||||
/// <returns>True if it can be assigned, otherwise false.</returns>
|
||||
protected virtual bool IsValid(Object obj)
|
||||
{
|
||||
var type = TypeUtils.GetObjectType(obj);
|
||||
return obj == null || _type.IsAssignableFrom(type) && (CheckValid == null || CheckValid(obj, type));
|
||||
@@ -168,6 +164,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
Focus();
|
||||
}, PresenterContext);
|
||||
}
|
||||
else if (new ScriptType(typeof(Control)).IsAssignableFrom(_type))
|
||||
{
|
||||
ActorSearchPopup.Show(this, new Float2(0, Height), IsValid, actor =>
|
||||
{
|
||||
Value = actor as UIControl;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
}, PresenterContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptSearchPopup.Show(this, new Float2(0, Height), IsValid, script =>
|
||||
|
||||
@@ -41,6 +41,7 @@ public class Editor : EditorModule
|
||||
options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter");
|
||||
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
||||
options.ScriptingAPI.SystemReferences.Add("System.IO.Compression.ZipFile");
|
||||
options.ScriptingAPI.SystemReferences.Add("System.Diagnostics.Process");
|
||||
if (Profiler.Use(options))
|
||||
options.ScriptingAPI.Defines.Add("USE_PROFILER");
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace FlaxEditor.Gizmo
|
||||
desc.CullMode = CullMode.TwoSided;
|
||||
desc.VS = _shader.GPU.GetVS("VS_Grid");
|
||||
desc.PS = _shader.GPU.GetPS("PS_Grid");
|
||||
desc.DepthWriteEnable = false;
|
||||
_psGrid.Init(ref desc);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace FlaxEditor.Modules
|
||||
public override Undo Undo => Editor.Instance.Undo;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override List<SceneGraphNode> Selection => _editor.SceneEditing.Selection;
|
||||
public override ISceneEditingContext SceneContext => _editor.Windows.EditWin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -234,7 +234,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
{
|
||||
if (methodGenericMap.TryGetValue(type.Name, out var methodIndex))
|
||||
return "``" + methodIndex;
|
||||
return "`" + typeGenericMap[type.Name];
|
||||
if (typeGenericMap.TryGetValue(type.Name, out var typeKey))
|
||||
return "`" + typeKey;
|
||||
return "`";
|
||||
}
|
||||
if (type.HasElementType)
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
|
||||
public override bool CanBeSelectedDirectly => true;
|
||||
|
||||
public override bool CanDuplicate => !Root?.Selection.Contains(ParentNode) ?? true;
|
||||
public override bool CanDuplicate => !Root?.SceneContext.Selection.Contains(ParentNode) ?? true;
|
||||
|
||||
public override bool CanDelete => true;
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
normal = -ray.Ray.Direction;
|
||||
var actor = (Spline)_node.Actor;
|
||||
var pos = actor.GetSplinePoint(Index);
|
||||
var nodeSize = NodeSizeByDistance(Transform.Translation, PointNodeSize);
|
||||
var nodeSize = NodeSizeByDistance(Transform.Translation, PointNodeSize, ray.View.Position);
|
||||
return new BoundingSphere(pos, nodeSize).Intersects(ref ray.Ray, out distance);
|
||||
}
|
||||
|
||||
@@ -186,9 +186,10 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
var pos = actor.GetSplinePoint(Index);
|
||||
var tangentIn = actor.GetSplineTangent(Index, true).Translation;
|
||||
var tangentOut = actor.GetSplineTangent(Index, false).Translation;
|
||||
var pointSize = NodeSizeByDistance(pos, PointNodeSize);
|
||||
var tangentInSize = NodeSizeByDistance(tangentIn, TangentNodeSize);
|
||||
var tangentOutSize = NodeSizeByDistance(tangentOut, TangentNodeSize);
|
||||
var cameraTransform = Root.SceneContext.Viewport.ViewTransform.Translation;
|
||||
var pointSize = NodeSizeByDistance(pos, PointNodeSize, cameraTransform);
|
||||
var tangentInSize = NodeSizeByDistance(tangentIn, TangentNodeSize, cameraTransform);
|
||||
var tangentOutSize = NodeSizeByDistance(tangentOut, TangentNodeSize, cameraTransform);
|
||||
|
||||
// Draw spline path
|
||||
ParentNode.OnDebugDraw(data);
|
||||
@@ -262,7 +263,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
normal = -ray.Ray.Direction;
|
||||
var actor = (Spline)_node.Actor;
|
||||
var pos = actor.GetSplineTangent(_index, _isIn).Translation;
|
||||
var tangentSize = NodeSizeByDistance(Transform.Translation, TangentNodeSize);
|
||||
var tangentSize = NodeSizeByDistance(Transform.Translation, TangentNodeSize, ray.View.Position);
|
||||
return new BoundingSphere(pos, tangentSize).Intersects(ref ray.Ray, out distance);
|
||||
}
|
||||
|
||||
@@ -274,7 +275,8 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
// Draw selected tangent highlight
|
||||
var actor = (Spline)_node.Actor;
|
||||
var pos = actor.GetSplineTangent(_index, _isIn).Translation;
|
||||
var tangentSize = NodeSizeByDistance(Transform.Translation, TangentNodeSize);
|
||||
var cameraTransform = Root.SceneContext.Viewport.ViewTransform.Translation;
|
||||
var tangentSize = NodeSizeByDistance(Transform.Translation, TangentNodeSize, cameraTransform);
|
||||
DebugDraw.DrawSphere(new BoundingSphere(pos, tangentSize), Color.YellowGreen, 0, false);
|
||||
}
|
||||
|
||||
@@ -306,9 +308,16 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
|
||||
private void OnUpdate()
|
||||
{
|
||||
// Prevent update event called when actor got deleted (incorrect state)
|
||||
if (!Actor)
|
||||
{
|
||||
FlaxEngine.Scripting.Update -= OnUpdate;
|
||||
return;
|
||||
}
|
||||
|
||||
// If this node's point is selected
|
||||
var selection = Editor.Instance.SceneEditing.Selection;
|
||||
if (selection.Count == 1 && selection[0] is SplinePointNode selectedPoint && selectedPoint.ParentNode == this)
|
||||
var selection = Root?.SceneContext.Selection;
|
||||
if (selection != null && selection.Count == 1 && selection[0] is SplinePointNode selectedPoint && selectedPoint.ParentNode == this)
|
||||
{
|
||||
var mouse = Input.Mouse;
|
||||
var keyboard = Input.Keyboard;
|
||||
@@ -317,7 +326,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
EditSplineWithSnap(selectedPoint);
|
||||
|
||||
var canAddSplinePoint = mouse.PositionDelta == Float2.Zero && mouse.Position != Float2.Zero;
|
||||
var requestAddSplinePoint = Input.Keyboard.GetKey(KeyboardKeys.Control) && mouse.GetButtonDown(MouseButton.Right);
|
||||
var requestAddSplinePoint = keyboard.GetKey(KeyboardKeys.Control) && mouse.GetButtonDown(MouseButton.Right);
|
||||
if (requestAddSplinePoint && canAddSplinePoint)
|
||||
AddSplinePoint(selectedPoint);
|
||||
}
|
||||
@@ -338,9 +347,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
while (srcCount > dstCount)
|
||||
{
|
||||
var node = ActorChildNodes[srcCount-- - 1];
|
||||
// TODO: support selection interface inside SceneGraph nodes (eg. on Root) so prefab editor can handle this too
|
||||
if (Editor.Instance.SceneEditing.Selection.Contains(node))
|
||||
Editor.Instance.SceneEditing.Deselect();
|
||||
Root?.SceneContext.Deselect(node);
|
||||
node.Dispose();
|
||||
}
|
||||
|
||||
@@ -356,22 +363,23 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void AddSplinePoint(SplinePointNode selectedPoint)
|
||||
private void AddSplinePoint(SplinePointNode selectedPoint)
|
||||
{
|
||||
// Check mouse hit on scene
|
||||
var root = Root;
|
||||
var spline = (Spline)Actor;
|
||||
var viewport = Editor.Instance.Windows.EditWin.Viewport;
|
||||
var viewport = root.SceneContext.Viewport;
|
||||
var mouseRay = viewport.MouseRay;
|
||||
var viewRay = viewport.ViewRay;
|
||||
var flags = RayCastData.FlagTypes.SkipColliders | RayCastData.FlagTypes.SkipEditorPrimitives;
|
||||
var hit = Editor.Instance.Scene.Root.RayCast(ref mouseRay, ref viewRay, out var closest, out var normal, flags);
|
||||
var hit = root.RayCast(ref mouseRay, ref viewRay, out var closest, out var normal, flags);
|
||||
if (hit == null)
|
||||
return;
|
||||
|
||||
// Undo data
|
||||
var oldSpline = spline.SplineKeyframes;
|
||||
var editAction = new EditSplineAction(spline, oldSpline);
|
||||
Root.Undo.AddAction(editAction);
|
||||
root.Undo.AddAction(editAction);
|
||||
|
||||
// Get spline point to duplicate
|
||||
var hitPoint = mouseRay.Position + mouseRay.Direction * closest;
|
||||
@@ -423,7 +431,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
|
||||
// Select new point node
|
||||
SyncSplineKeyframeWithNodes();
|
||||
Editor.Instance.SceneEditing.Select(ChildNodes[newPointIndex]);
|
||||
root.SceneContext.Select(ChildNodes[newPointIndex]);
|
||||
|
||||
spline.UpdateSpline();
|
||||
}
|
||||
@@ -436,6 +444,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
allSplinesInView.Remove(spline);
|
||||
if (allSplinesInView.Count == 0)
|
||||
return;
|
||||
var cameraTransform = Root.SceneContext.Viewport.ViewTransform.Translation;
|
||||
|
||||
var snappedOnSplinePoint = false;
|
||||
for (int i = 0; i < allSplinesInView.Count; i++)
|
||||
@@ -443,7 +452,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
for (int x = 0; x < allSplinesInView[i].SplineKeyframes.Length; x++)
|
||||
{
|
||||
var keyframePosition = allSplinesInView[i].GetSplinePoint(x);
|
||||
var pointIndicatorSize = NodeSizeByDistance(keyframePosition, SnapPointIndicatorSize);
|
||||
var pointIndicatorSize = NodeSizeByDistance(keyframePosition, SnapPointIndicatorSize, cameraTransform);
|
||||
var keyframeBounds = new BoundingSphere(keyframePosition, pointIndicatorSize);
|
||||
DebugDraw.DrawSphere(keyframeBounds, Color.Red, 0, false);
|
||||
|
||||
@@ -459,7 +468,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
if (!snappedOnSplinePoint)
|
||||
{
|
||||
var nearSplineSnapPoint = GetNearSplineSnapPosition(selectedPoint.Transform.Translation, allSplinesInView);
|
||||
var snapIndicatorSize = NodeSizeByDistance(nearSplineSnapPoint, SnapIndicatorSize);
|
||||
var snapIndicatorSize = NodeSizeByDistance(nearSplineSnapPoint, SnapIndicatorSize, cameraTransform);
|
||||
var snapBounds = new BoundingSphere(nearSplineSnapPoint, snapIndicatorSize);
|
||||
if (snapBounds.Intersects(selectedPointBounds))
|
||||
{
|
||||
@@ -551,11 +560,16 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Spline> GetSplinesInView()
|
||||
private List<Spline> GetSplinesInView()
|
||||
{
|
||||
var splines = Level.GetActors<Spline>(true);
|
||||
Spline[] splines;
|
||||
var sceneContext = Root.SceneContext;
|
||||
if (sceneContext is Windows.Assets.PrefabWindow prefabWindow)
|
||||
splines = new Spline[0]; // TODO: add GetActors or similar utility to SceneContext and use Level.GetActors<Spline>(..) with specific root actor
|
||||
else
|
||||
splines = Level.GetActors<Spline>(true);
|
||||
var result = new List<Spline>();
|
||||
var viewBounds = Editor.Instance.Windows.EditWin.Viewport.ViewFrustum;
|
||||
var viewBounds = sceneContext.Viewport.ViewFrustum;
|
||||
foreach (var s in splines)
|
||||
{
|
||||
var contains = viewBounds.Contains(s.EditorBox);
|
||||
@@ -569,7 +583,6 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
var nearPoint = splines[0].GetSplinePointClosestToPoint(position);
|
||||
var nearDistance = Vector3.Distance(nearPoint, position);
|
||||
|
||||
for (int i = 1; i < splines.Count; i++)
|
||||
{
|
||||
var point = splines[i].GetSplinePointClosestToPoint(position);
|
||||
@@ -580,14 +593,12 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
nearDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return nearPoint;
|
||||
}
|
||||
|
||||
internal static Real NodeSizeByDistance(Vector3 nodePosition, Real nodeSize)
|
||||
internal static Real NodeSizeByDistance(Vector3 nodePosition, Real nodeSize, Vector3 viewPosition)
|
||||
{
|
||||
var cameraTransform = Editor.Instance.Windows.EditWin.Viewport.ViewportCamera.Viewport.ViewTransform;
|
||||
var distance = Vector3.Distance(cameraTransform.Translation, nodePosition) / 100;
|
||||
var distance = Vector3.Distance(viewPosition, nodePosition) / 100;
|
||||
return distance * nodeSize;
|
||||
}
|
||||
|
||||
|
||||
47
Source/Editor/SceneGraph/ISceneEditingContext.cs
Normal file
47
Source/Editor/SceneGraph/ISceneEditingContext.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Viewport;
|
||||
|
||||
namespace FlaxEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Shared interface for scene editing utilities.
|
||||
/// </summary>
|
||||
public interface ISceneEditingContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the main or last editor viewport used for scene editing within this context.
|
||||
/// </summary>
|
||||
EditorViewport Viewport { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of selected scene graph nodes in the editor context.
|
||||
/// </summary>
|
||||
List<SceneGraphNode> Selection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Selects the specified node.
|
||||
/// </summary>
|
||||
/// <param name="node">The node.</param>
|
||||
/// <param name="additive">if set to <c>true</c> will use additive mode, otherwise will clear previous selection.</param>
|
||||
void Select(SceneGraphNode node, bool additive = false);
|
||||
|
||||
/// <summary>
|
||||
/// Deselects node.
|
||||
/// </summary>
|
||||
/// <param name="node">The node to deselect.</param>
|
||||
void Deselect(SceneGraphNode node);
|
||||
|
||||
/// <summary>
|
||||
/// Opends popup for renaming selected objects.
|
||||
/// </summary>
|
||||
void RenameSelection();
|
||||
|
||||
/// <summary>
|
||||
/// Focuses selected objects.
|
||||
/// </summary>
|
||||
void FocusSelection();
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,14 @@ namespace FlaxEditor.SceneGraph
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of selected scene graph nodes in the editor context.
|
||||
/// [Deprecated in v1.10]
|
||||
/// </summary>
|
||||
public abstract List<SceneGraphNode> Selection { get; }
|
||||
[Obsolete("Use SceneContext.Selection instead.")]
|
||||
public List<SceneGraphNode> Selection => SceneContext.Selection;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of selected scene graph nodes in the editor context.
|
||||
/// </summary>
|
||||
public abstract ISceneEditingContext SceneContext { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1557,11 +1557,11 @@ namespace FlaxEditor.Utilities
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static ISceneContextWindow GetSceneContext(this Control c)
|
||||
internal static ISceneEditingContext GetSceneContext(this Control c)
|
||||
{
|
||||
while (c != null && !(c is ISceneContextWindow))
|
||||
while (c != null && !(c is ISceneEditingContext))
|
||||
c = c.Parent;
|
||||
return c as ISceneContextWindow;
|
||||
return c as ISceneEditingContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,6 +968,14 @@ namespace FlaxEditor.Viewport
|
||||
debugView.VisibleChanged += WidgetViewModeShowHide;
|
||||
}
|
||||
|
||||
// Clear Debug Draw
|
||||
{
|
||||
var button = ViewWidgetButtonMenu.AddButton("Clear Debug Draw");
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Clicked += () => DebugDraw.Clear();
|
||||
ViewWidgetButtonMenu.VisibleChanged += (Control cm) => { button.Visible = DebugDraw.CanClear(); };
|
||||
}
|
||||
|
||||
ViewWidgetButtonMenu.AddSeparator();
|
||||
|
||||
// Brightness
|
||||
|
||||
@@ -10,7 +10,6 @@ using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
public override Undo Undo => _window.Undo;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override List<SceneGraphNode> Selection => _window.Selection;
|
||||
public override ISceneEditingContext SceneContext => _window;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,10 +12,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
public sealed partial class PrefabWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// The current selection (readonly).
|
||||
/// </summary>
|
||||
public readonly List<SceneGraphNode> Selection = new List<SceneGraphNode>();
|
||||
private readonly List<SceneGraphNode> _selection = new List<SceneGraphNode>();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when selection gets changed.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors;
|
||||
@@ -19,7 +20,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// </summary>
|
||||
/// <seealso cref="Prefab" />
|
||||
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
||||
public sealed partial class PrefabWindow : AssetEditorWindowBase<Prefab>, IPresenterOwner, ISceneContextWindow
|
||||
public sealed partial class PrefabWindow : AssetEditorWindowBase<Prefab>, IPresenterOwner, ISceneEditingContext
|
||||
{
|
||||
private readonly SplitPanel _split1;
|
||||
private readonly SplitPanel _split2;
|
||||
@@ -54,6 +55,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// </summary>
|
||||
public PrefabWindowViewport Viewport => _viewport;
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<SceneGraphNode> Selection => _selection;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the prefab objects properties editor.
|
||||
/// </summary>
|
||||
@@ -559,5 +563,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
/// <inheritdoc />
|
||||
public EditorViewport PresenterViewport => _viewport;
|
||||
|
||||
/// <inheritdoc />
|
||||
EditorViewport ISceneEditingContext.Viewport => Viewport;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,15 @@ namespace FlaxEditor.Windows
|
||||
});
|
||||
|
||||
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(item.Path)));
|
||||
|
||||
if (!String.IsNullOrEmpty(Editor.Instance.Windows.ContentWin._itemsSearchBox.Text))
|
||||
{
|
||||
cm.AddButton("Show in Content Panel", () =>
|
||||
{
|
||||
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
|
||||
Editor.Instance.Windows.ContentWin.Select(item);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.HasDefaultThumbnail == false)
|
||||
{
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace FlaxEditor.Windows
|
||||
/// <summary>
|
||||
/// The viewport control.
|
||||
/// </summary>
|
||||
public readonly MainEditorGizmoViewport Viewport;
|
||||
public new readonly MainEditorGizmoViewport Viewport;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EditGameWindow"/> class.
|
||||
|
||||
@@ -2,16 +2,20 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Tabs;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
using Debug = FlaxEngine.Debug;
|
||||
using PlatformType = FlaxEngine.PlatformType;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable MemberCanBePrivate.Local
|
||||
@@ -192,6 +196,12 @@ namespace FlaxEditor.Windows
|
||||
var label = layout.Label(text, TextAlignment.Center);
|
||||
label.Label.AutoHeight = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to add platform specific tools if available.
|
||||
/// </summary>
|
||||
/// <param name="layout">The layout to start the tools at.</param>
|
||||
public virtual void OnCustomToolsLayout(LayoutElementsContainer layout) { }
|
||||
|
||||
public virtual void Build()
|
||||
{
|
||||
@@ -233,6 +243,272 @@ namespace FlaxEditor.Windows
|
||||
class Android : Platform
|
||||
{
|
||||
protected override BuildPlatform BuildPlatform => BuildPlatform.AndroidARM64;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnCustomToolsLayout(LayoutElementsContainer layout)
|
||||
{
|
||||
base.OnCustomToolsLayout(layout);
|
||||
|
||||
// Add emulation options to android tab.
|
||||
layout.Space(5);
|
||||
var emulatorGroup = layout.Group("Tools");
|
||||
var sdkPath = Environment.GetEnvironmentVariable("ANDROID_HOME");
|
||||
if (string.IsNullOrEmpty(sdkPath))
|
||||
sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK");
|
||||
emulatorGroup.Label($"SDK path: {sdkPath}");
|
||||
|
||||
// AVD and starting emulator
|
||||
var avdGroup = emulatorGroup.Group("AVD Emulator");
|
||||
avdGroup.Label("Note: Create AVDs using Android Studio.");
|
||||
avdGroup.Panel.IsClosed = false;
|
||||
var refreshAVDListButton = avdGroup.Button("Refresh AVD list").Button;
|
||||
var avdListGroup = avdGroup.Group("AVD List");
|
||||
avdListGroup.Panel.IsClosed = false;
|
||||
var noAvdLabel = avdListGroup.Label("No AVDs detected. Click Refresh.", TextAlignment.Center).Label;
|
||||
var avdListTree = new Tree(false)
|
||||
{
|
||||
Parent = avdListGroup.Panel,
|
||||
};
|
||||
refreshAVDListButton.Clicked += () =>
|
||||
{
|
||||
if (avdListTree.Children.Count > 0)
|
||||
avdListTree.DisposeChildren();
|
||||
|
||||
var processStartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"),
|
||||
Arguments = "-list-avds",
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
|
||||
var process = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = processStartInfo
|
||||
};
|
||||
process.Start();
|
||||
var output = new string(process.StandardOutput.ReadToEnd());
|
||||
/*
|
||||
CreateProcessSettings processSettings = new CreateProcessSettings
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"),
|
||||
Arguments = "-list-avds",
|
||||
HiddenWindow = false,
|
||||
SaveOutput = true,
|
||||
WaitForEnd = true,
|
||||
};
|
||||
//processSettings.ShellExecute = true;
|
||||
FlaxEngine.Platform.CreateProcess(ref processSettings);
|
||||
|
||||
var output = new string(processSettings.Output);*/
|
||||
if (output.Length == 0)
|
||||
{
|
||||
noAvdLabel.Visible = true;
|
||||
FlaxEditor.Editor.LogWarning("No AVDs detected.");
|
||||
return;
|
||||
}
|
||||
noAvdLabel.Visible = false;
|
||||
var splitOutput = output.Split('\n');
|
||||
foreach (var line in splitOutput)
|
||||
{
|
||||
if (string.IsNullOrEmpty(line.Trim()))
|
||||
continue;
|
||||
var item = new TreeNode
|
||||
{
|
||||
Text = line.Trim(),
|
||||
Parent = avdListTree,
|
||||
};
|
||||
}
|
||||
avdListGroup.Panel.IsClosed = false;
|
||||
};
|
||||
|
||||
avdGroup.Label("Emulator AVD Commands:");
|
||||
var commandsTextBox = avdGroup.TextBox().TextBox;
|
||||
commandsTextBox.IsMultiline = false;
|
||||
commandsTextBox.Text = "-no-snapshot-load -no-boot-anim"; // TODO: save user changes
|
||||
|
||||
var startEmulatorButton = avdGroup.Button("Start AVD Emulator").Button;
|
||||
startEmulatorButton.TooltipText = "Starts selected AVD from list.";
|
||||
startEmulatorButton.Clicked += () =>
|
||||
{
|
||||
if (avdListTree.Selection.Count == 0)
|
||||
return;
|
||||
|
||||
CreateProcessSettings processSettings = new CreateProcessSettings
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "emulator", "emulator.exe"),
|
||||
Arguments = $"-avd {avdListTree.Selection[0].Text} {commandsTextBox.Text}",
|
||||
HiddenWindow = true,
|
||||
SaveOutput = false,
|
||||
WaitForEnd = false,
|
||||
};
|
||||
processSettings.ShellExecute = true;
|
||||
FlaxEngine.Platform.CreateProcess(ref processSettings);
|
||||
};
|
||||
|
||||
emulatorGroup.Space(2);
|
||||
|
||||
// Device
|
||||
var installGroup = emulatorGroup.Group("Install");
|
||||
installGroup.Panel.IsClosed = false;
|
||||
installGroup.Label("Note: Used to install to AVD or physical devices.");
|
||||
var refreshDeviceListButton = installGroup.Button("Refresh device list").Button;
|
||||
var deviceListGroup = installGroup.Group("List of devices");
|
||||
deviceListGroup.Panel.IsClosed = false;
|
||||
var noDevicesLabel = deviceListGroup.Label("No devices found. Click Refresh.", TextAlignment.Center).Label;
|
||||
var deviceListTree = new Tree(false)
|
||||
{
|
||||
Parent = deviceListGroup.Panel,
|
||||
};
|
||||
refreshDeviceListButton.Clicked += () =>
|
||||
{
|
||||
if (deviceListTree.Children.Count > 0)
|
||||
deviceListTree.DisposeChildren();
|
||||
|
||||
var processStartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"),
|
||||
Arguments = "devices -l",
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
|
||||
var process = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = processStartInfo
|
||||
};
|
||||
process.Start();
|
||||
var output = new string(process.StandardOutput.ReadToEnd());
|
||||
/*
|
||||
CreateProcessSettings processSettings = new CreateProcessSettings
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"),
|
||||
Arguments = "devices -l",
|
||||
HiddenWindow = false,
|
||||
//SaveOutput = true,
|
||||
WaitForEnd = true,
|
||||
};
|
||||
processSettings.SaveOutput = true;
|
||||
processSettings.ShellExecute = false;
|
||||
FlaxEngine.Platform.CreateProcess(ref processSettings);
|
||||
|
||||
var output = new string(processSettings.Output);
|
||||
*/
|
||||
|
||||
if (output.Length > 0 && !output.Equals("List of devices attached", StringComparison.Ordinal))
|
||||
{
|
||||
noDevicesLabel.Visible = false;
|
||||
var splitLines = output.Split('\n');
|
||||
foreach (var line in splitLines)
|
||||
{
|
||||
if (line.Trim().Equals("List of devices attached", StringComparison.Ordinal) || string.IsNullOrEmpty(line.Trim()))
|
||||
continue;
|
||||
|
||||
var tab = line.Split("device ");
|
||||
if (tab.Length < 2)
|
||||
continue;
|
||||
var item = new TreeNode
|
||||
{
|
||||
Text = $"{tab[0].Trim()} - {tab[1].Trim()}",
|
||||
Tag = tab[0].Trim(),
|
||||
Parent = deviceListTree,
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
noDevicesLabel.Visible = true;
|
||||
}
|
||||
|
||||
deviceListGroup.Panel.IsClosed = false;
|
||||
};
|
||||
|
||||
var autoStart = installGroup.Checkbox("Try to auto start activity on device.");
|
||||
var installButton = installGroup.Button("Install APK to Device").Button;
|
||||
installButton.TooltipText = "Installs APK from the output folder to the selected device.";
|
||||
installButton.Clicked += () =>
|
||||
{
|
||||
if (deviceListTree.Selection.Count == 0)
|
||||
return;
|
||||
|
||||
// Get built APK at output path
|
||||
string output = StringUtils.ConvertRelativePathToAbsolute(Globals.ProjectFolder, StringUtils.NormalizePath(Output));
|
||||
if (!Directory.Exists(output))
|
||||
{
|
||||
FlaxEditor.Editor.LogWarning("Can not copy APK because output folder does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
var apkFiles = Directory.GetFiles(output, "*.apk");
|
||||
if (apkFiles.Length == 0)
|
||||
{
|
||||
FlaxEditor.Editor.LogWarning("Can not copy APK because no .apk files were found in output folder.");
|
||||
return;
|
||||
}
|
||||
|
||||
string apkFilesString = string.Empty;
|
||||
for (int i = 0; i < apkFiles.Length; i++)
|
||||
{
|
||||
var file = apkFiles[i];
|
||||
if (i == 0)
|
||||
{
|
||||
apkFilesString = $"\"{file}\"";
|
||||
continue;
|
||||
}
|
||||
apkFilesString += $" \"{file}\"";
|
||||
}
|
||||
|
||||
CreateProcessSettings processSettings = new CreateProcessSettings
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"),
|
||||
Arguments = $"-s {deviceListTree.Selection[0].Tag} {(apkFiles.Length > 1 ? "install-multiple" : "install")} {apkFilesString}",
|
||||
LogOutput = true,
|
||||
};
|
||||
FlaxEngine.Platform.CreateProcess(ref processSettings);
|
||||
|
||||
if (autoStart.CheckBox.Checked)
|
||||
{
|
||||
var gameSettings = GameSettings.Load();
|
||||
var productName = gameSettings.ProductName.Replace(" ", "").ToLower();
|
||||
var companyName = gameSettings.CompanyName.Replace(" ", "").ToLower();
|
||||
CreateProcessSettings processSettings1 = new CreateProcessSettings
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"),
|
||||
Arguments = $"shell am start -n com.{companyName}.{productName}/com.flaxengine.GameActivity",
|
||||
LogOutput = true,
|
||||
};
|
||||
FlaxEngine.Platform.CreateProcess(ref processSettings1);
|
||||
}
|
||||
};
|
||||
|
||||
var adbLogButton = emulatorGroup.Button("Start adb log collecting").Button;
|
||||
adbLogButton.TooltipText = "In debug and development builds the engine and game logs can be output directly to the adb.";
|
||||
adbLogButton.Clicked += () =>
|
||||
{
|
||||
var processStartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"),
|
||||
Arguments = "logcat Flax:I *:S",
|
||||
CreateNoWindow = false,
|
||||
WindowStyle = ProcessWindowStyle.Normal,
|
||||
};
|
||||
|
||||
var process = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = processStartInfo
|
||||
};
|
||||
process.Start();
|
||||
/*
|
||||
CreateProcessSettings processSettings = new CreateProcessSettings
|
||||
{
|
||||
FileName = Path.Combine(sdkPath, "platform-tools", "adb.exe"),
|
||||
Arguments = $"logcat Flax:I *:S",
|
||||
WaitForEnd = false,
|
||||
};
|
||||
FlaxEngine.Platform.CreateProcess(ref processSettings);
|
||||
*/
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Switch : Platform
|
||||
@@ -343,6 +619,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
_buildButton = layout.Button("Build").Button;
|
||||
_buildButton.Clicked += OnBuildClicked;
|
||||
platformObj.OnCustomToolsLayout(layout);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -117,7 +117,6 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (!AudioMuted)
|
||||
Audio.MasterVolume = value;
|
||||
|
||||
_audioVolume = value;
|
||||
}
|
||||
}
|
||||
@@ -679,6 +678,14 @@ namespace FlaxEditor.Windows
|
||||
checkbox.StateChanged += x => ShowDebugDraw = x.Checked;
|
||||
}
|
||||
|
||||
// Clear Debug Draw
|
||||
if (DebugDraw.CanClear())
|
||||
{
|
||||
var button = menu.AddButton("Clear Debug Draw");
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Clicked += () => DebugDraw.Clear();
|
||||
}
|
||||
|
||||
menu.AddSeparator();
|
||||
|
||||
// Mute Audio
|
||||
|
||||
@@ -1,32 +1,18 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Viewport;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Shared interface for scene editing utilities.
|
||||
/// </summary>
|
||||
public interface ISceneContextWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Opends popup for renaming selected objects.
|
||||
/// </summary>
|
||||
void RenameSelection();
|
||||
|
||||
/// <summary>
|
||||
/// Focuses selected objects.
|
||||
/// </summary>
|
||||
void FocusSelection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for editor windows dedicated to scene editing.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Windows.EditorWindow" />
|
||||
public abstract class SceneEditorWindow : EditorWindow, ISceneContextWindow
|
||||
public abstract class SceneEditorWindow : EditorWindow, ISceneEditingContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SceneEditorWindow"/> class.
|
||||
@@ -46,6 +32,24 @@ namespace FlaxEditor.Windows
|
||||
Editor.Windows.EditWin.Viewport.FocusSelection();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EditorViewport Viewport => Editor.Windows.EditWin.Viewport;
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<SceneGraphNode> Selection => Editor.SceneEditing.Selection;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Select(SceneGraphNode node, bool additive = false)
|
||||
{
|
||||
Editor.SceneEditing.Select(node, additive);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Deselect(SceneGraphNode node)
|
||||
{
|
||||
Editor.SceneEditing.Deselect(node);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RenameSelection()
|
||||
{
|
||||
|
||||
@@ -1051,6 +1051,7 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
|
||||
LOG(Error, "Cannot create virtual asset object.");
|
||||
return nullptr;
|
||||
}
|
||||
asset->RegisterObject();
|
||||
|
||||
// Call initializer function
|
||||
asset->InitAsVirtual();
|
||||
@@ -1291,6 +1292,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
LOAD_FAILED();
|
||||
}
|
||||
#endif
|
||||
result->RegisterObject();
|
||||
|
||||
// Register asset
|
||||
AssetsLocker.Lock();
|
||||
|
||||
@@ -348,6 +348,11 @@ struct DebugDrawContext
|
||||
DebugDrawData DebugDrawDepthTest;
|
||||
Float3 LastViewPos = Float3::Zero;
|
||||
Matrix LastViewProj = Matrix::Identity;
|
||||
|
||||
inline int32 Count() const
|
||||
{
|
||||
return DebugDrawDefault.Count() + DebugDrawDepthTest.Count();
|
||||
}
|
||||
};
|
||||
|
||||
namespace
|
||||
@@ -748,6 +753,13 @@ void DebugDraw::SetContext(void* context)
|
||||
Context = context ? (DebugDrawContext*)context : &GlobalContext;
|
||||
}
|
||||
|
||||
bool DebugDraw::CanClear(void* context)
|
||||
{
|
||||
if (!context)
|
||||
context = &GlobalContext;
|
||||
return ((DebugDrawContext*)context)->Count() != 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Vector3 DebugDraw::GetViewPos()
|
||||
|
||||
@@ -65,6 +65,13 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
||||
/// </summary>
|
||||
/// <param name="context">The context or null.</param>
|
||||
API_FUNCTION() static void SetContext(void* context);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if can clear all debug shapes displayed on screen. Can be used to disable this functionality when not needed for the user.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <returns>True fi context can be cleared (has any shapes), otherwise false.</returns>
|
||||
API_FUNCTION() static bool CanClear(void* context = nullptr);
|
||||
#endif
|
||||
|
||||
// Gets the last view position when rendering the current context. Can be sued for custom culling or LODing when drawing more complex shapes.
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
static void Log(LogType type, const StringView& message);
|
||||
|
||||
/// <summary>
|
||||
/// A variant of Debug.Log that logs a warning message to the console.
|
||||
/// A variant of Debug.Log that logs an info message to the console.
|
||||
/// </summary>
|
||||
/// <param name="message">The text message to display.</param>
|
||||
FORCE_INLINE static void Log(const StringView& message)
|
||||
|
||||
@@ -613,6 +613,209 @@ float Input::GetAxisRaw(const StringView& name)
|
||||
return e ? e->ValueRaw : false;
|
||||
}
|
||||
|
||||
void Input::SetInputMappingFromSettings(const JsonAssetReference<InputSettings>& settings)
|
||||
{
|
||||
auto actionMappings = settings.GetInstance()->ActionMappings;
|
||||
ActionMappings.Resize(actionMappings.Count(), false);
|
||||
for (int i = 0; i < actionMappings.Count(); i++)
|
||||
{
|
||||
ActionMappings[i] = actionMappings.At(i);
|
||||
}
|
||||
|
||||
auto axisMappings = settings.GetInstance()->AxisMappings;
|
||||
AxisMappings.Resize(axisMappings.Count(), false);
|
||||
for (int i = 0; i < axisMappings.Count(); i++)
|
||||
{
|
||||
AxisMappings[i] = axisMappings.At(i);
|
||||
}
|
||||
Axes.Clear();
|
||||
Actions.Clear();
|
||||
}
|
||||
|
||||
void Input::SetInputMappingToDefaultSettings()
|
||||
{
|
||||
InputSettings* settings = InputSettings::Get();
|
||||
if (settings)
|
||||
{
|
||||
ActionMappings = settings->ActionMappings;
|
||||
AxisMappings = settings->AxisMappings;
|
||||
Axes.Clear();
|
||||
Actions.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
ActionConfig Input::GetActionConfigByName(const StringView& name)
|
||||
{
|
||||
ActionConfig config = {};
|
||||
for (const auto& a : ActionMappings)
|
||||
{
|
||||
if (a.Name == name)
|
||||
{
|
||||
config = a;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
Array<ActionConfig> Input::GetAllActionConfigsByName(const StringView& name)
|
||||
{
|
||||
Array<ActionConfig> actionConfigs;
|
||||
for (const auto& a : ActionMappings)
|
||||
{
|
||||
if (a.Name == name)
|
||||
actionConfigs.Add(a);
|
||||
}
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
AxisConfig Input::GetAxisConfigByName(const StringView& name)
|
||||
{
|
||||
AxisConfig config = {};
|
||||
for (const auto& a : AxisMappings)
|
||||
{
|
||||
if (a.Name == name)
|
||||
{
|
||||
config = a;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
Array<AxisConfig> Input::GetAllAxisConfigsByName(const StringView& name)
|
||||
{
|
||||
Array<AxisConfig> actionConfigs;
|
||||
for (const auto& a : AxisMappings)
|
||||
{
|
||||
if (a.Name == name)
|
||||
actionConfigs.Add(a);
|
||||
}
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
void Input::SetAxisConfigByName(const StringView& name, AxisConfig& config, bool all)
|
||||
{
|
||||
for (int i = 0; i < AxisMappings.Count(); ++i)
|
||||
{
|
||||
auto& mapping = AxisMappings.At(i);
|
||||
if (mapping.Name.Compare(name.ToString()) == 0)
|
||||
{
|
||||
if (config.Name.IsEmpty())
|
||||
config.Name = name;
|
||||
mapping = config;
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input::SetAxisConfigByName(const StringView& name, InputAxisType inputType, const KeyboardKeys positiveButton, const KeyboardKeys negativeButton, bool all)
|
||||
{
|
||||
for (int i = 0; i < AxisMappings.Count(); ++i)
|
||||
{
|
||||
auto& mapping = AxisMappings.At(i);
|
||||
if (mapping.Name.Compare(name.ToString()) == 0 && mapping.Axis == inputType)
|
||||
{
|
||||
mapping.PositiveButton = positiveButton;
|
||||
mapping.NegativeButton = negativeButton;
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input::SetAxisConfigByName(const StringView& name, InputAxisType inputType, const GamepadButton positiveButton, const GamepadButton negativeButton, InputGamepadIndex gamepadIndex, bool all)
|
||||
{
|
||||
for (int i = 0; i < AxisMappings.Count(); ++i)
|
||||
{
|
||||
auto& mapping = AxisMappings.At(i);
|
||||
if (mapping.Name.Compare(name.ToString()) == 0 && mapping.Gamepad == gamepadIndex && mapping.Axis == inputType)
|
||||
{
|
||||
mapping.GamepadPositiveButton = positiveButton;
|
||||
mapping.GamepadNegativeButton = negativeButton;
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input::SetAxisConfigByName(const StringView& name, InputAxisType inputType, const float gravity, const float deadZone, const float sensitivity, const float scale, const bool snap, bool all)
|
||||
{
|
||||
for (int i = 0; i < AxisMappings.Count(); ++i)
|
||||
{
|
||||
auto& mapping = AxisMappings.At(i);
|
||||
if (mapping.Name.Compare(name.ToString()) == 0 && mapping.Axis == inputType)
|
||||
{
|
||||
mapping.Gravity = gravity;
|
||||
mapping.DeadZone = deadZone;
|
||||
mapping.Sensitivity = sensitivity;
|
||||
mapping.Scale = scale;
|
||||
mapping.Snap = snap;
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input::SetActionConfigByName(const StringView& name, const KeyboardKeys key, bool all)
|
||||
{
|
||||
for (int i = 0; i < ActionMappings.Count(); ++i)
|
||||
{
|
||||
auto& mapping = ActionMappings.At(i);
|
||||
if (mapping.Name.Compare(name.ToString()) == 0)
|
||||
{
|
||||
mapping.Key = key;
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input::SetActionConfigByName(const StringView& name, const MouseButton mouseButton, bool all)
|
||||
{
|
||||
for (int i = 0; i < ActionMappings.Count(); ++i)
|
||||
{
|
||||
auto& mapping = ActionMappings.At(i);
|
||||
if (mapping.Name.Compare(name.ToString()) == 0)
|
||||
{
|
||||
mapping.MouseButton = mouseButton;
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input::SetActionConfigByName(const StringView& name, const GamepadButton gamepadButton, InputGamepadIndex gamepadIndex, bool all)
|
||||
{
|
||||
for (int i = 0; i < ActionMappings.Count(); ++i)
|
||||
{
|
||||
auto& mapping = ActionMappings.At(i);
|
||||
if (mapping.Name.Compare(name.ToString()) == 0 && mapping.Gamepad == gamepadIndex)
|
||||
{
|
||||
mapping.GamepadButton = gamepadButton;
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input::SetActionConfigByName(const StringView& name, ActionConfig& config, bool all)
|
||||
{
|
||||
for (int i = 0; i < ActionMappings.Count(); ++i)
|
||||
{
|
||||
auto& mapping = ActionMappings.At(i);
|
||||
if (mapping.Name.Compare(name.ToString()) == 0)
|
||||
{
|
||||
if (config.Name.IsEmpty())
|
||||
config.Name = name;
|
||||
mapping = config;
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputService::Update()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "KeyboardKeys.h"
|
||||
#include "Enums.h"
|
||||
#include "VirtualInput.h"
|
||||
#include "Engine/Content/JsonAssetReference.h"
|
||||
|
||||
class Mouse;
|
||||
class Keyboard;
|
||||
@@ -332,4 +333,118 @@ public:
|
||||
/// <returns>The current axis value (e.g for gamepads it's in the range -1..1). No smoothing applied.</returns>
|
||||
/// <seealso cref="AxisMappings"/>
|
||||
API_FUNCTION() static float GetAxisRaw(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Sets and overwrites the Action and Axis mappings with the values from a new InputSettings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The input settings.</param>
|
||||
API_FUNCTION() static void SetInputMappingFromSettings(const JsonAssetReference<class InputSettings>& settings);
|
||||
|
||||
/// <summary>
|
||||
/// Sets and overwrites the Action and Axis mappings with the values from the InputSettings in GameSettings.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void SetInputMappingToDefaultSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first action configuration by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <returns>The first Action configuration with the name. Empty configuration if not found.</returns>
|
||||
API_FUNCTION() static ActionConfig GetActionConfigByName(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the action configurations by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <returns>The Action configurations with the name.</returns>
|
||||
API_FUNCTION() static Array<ActionConfig> GetAllActionConfigsByName(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action configuration keyboard key by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <param name="key">The key to set.</param>
|
||||
/// <param name="all">Whether to set only the first config found or all of them.</param>
|
||||
API_FUNCTION() static void SetActionConfigByName(const StringView& name, const KeyboardKeys key, bool all = false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action configuration mouse button by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <param name="mouseButton">The mouse button to set.</param>
|
||||
/// <param name="all">Whether to set only the first config found or all of them.</param>
|
||||
API_FUNCTION() static void SetActionConfigByName(const StringView& name, const MouseButton mouseButton, bool all = false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action configuration gamepad button by name and index.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <param name="gamepadButton">The gamepad button to set.</param>
|
||||
/// <param name="gamepadIndex">The gamepad index used to find the correct config.</param>
|
||||
/// <param name="all">Whether to set only the first config found or all of them.</param>
|
||||
API_FUNCTION() static void SetActionConfigByName(const StringView& name, const GamepadButton gamepadButton, InputGamepadIndex gamepadIndex, bool all = false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action configuration by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <param name="config">The action configuration to set. Leave the config name empty to use set name.</param>
|
||||
/// <param name="all">Whether to set only the first config found or all of them.</param>
|
||||
API_FUNCTION() static void SetActionConfigByName(const StringView& name, ActionConfig& config, bool all = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first axis configurations by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the axis config.</param>
|
||||
/// <returns>The first Axis configuration with the name. Empty configuration if not found.</returns>
|
||||
API_FUNCTION() static AxisConfig GetAxisConfigByName(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the axis configurations by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the axis config.</param>
|
||||
/// <returns>The axis configurations with the name.</returns>
|
||||
API_FUNCTION() static Array<AxisConfig> GetAllAxisConfigsByName(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the axis configuration keyboard key by name and type.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <param name="config">The configuration to set. Leave the config name empty to use set name.</param>
|
||||
/// <param name="all">Whether to set only the first config found or all of them.</param>
|
||||
API_FUNCTION() static void SetAxisConfigByName(const StringView& name, AxisConfig& config, bool all = false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the axis configuration keyboard key buttons by name and type.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <param name="inputType">The type to sort by.</param>
|
||||
/// <param name="positiveButton">The positive key button.</param>
|
||||
/// <param name="negativeButton">The negative key button.</param>
|
||||
/// <param name="all">Whether to set only the first config found or all of them.</param>
|
||||
API_FUNCTION() static void SetAxisConfigByName(const StringView& name, InputAxisType inputType, const KeyboardKeys positiveButton, const KeyboardKeys negativeButton, bool all = false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the axis configuration gamepad buttons by name, type, and index.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <param name="inputType">The type to sort by.</param>
|
||||
/// <param name="positiveButton">The positive gamepad button.</param>
|
||||
/// <param name="negativeButton">The negative gamepad button.</param>
|
||||
/// <param name="gamepadIndex">The gamepad index to sort by.</param>
|
||||
/// <param name="all">Whether to set only the first config found or all of them.</param>
|
||||
API_FUNCTION() static void SetAxisConfigByName(const StringView& name, InputAxisType inputType, const GamepadButton positiveButton, const GamepadButton negativeButton, InputGamepadIndex gamepadIndex, bool all = false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the axis configuration accessories by name, and type.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action config.</param>
|
||||
/// <param name="inputType">The type to sort by.</param>
|
||||
/// <param name="gravity">The gravity to set.</param>
|
||||
/// <param name="deadZone">The dead zone to set.</param>
|
||||
/// <param name="sensitivity">The sensitivity to set.</param>
|
||||
/// <param name="scale">The scale to set.</param>
|
||||
/// <param name="snap">The snap to set.</param>
|
||||
/// <param name="all">Whether to set only the first config found or all of them.</param>
|
||||
API_FUNCTION() static void SetAxisConfigByName(const StringView& name, InputAxisType inputType, const float gravity, const float deadZone, const float sensitivity, const float scale, const bool snap, bool all = false);
|
||||
};
|
||||
|
||||
@@ -960,10 +960,12 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
objects[i] = obj;
|
||||
if (obj)
|
||||
{
|
||||
obj->RegisterObject();
|
||||
if (!obj->IsRegistered())
|
||||
obj->RegisterObject();
|
||||
#if USE_EDITOR
|
||||
// Auto-create C# objects for all actors in Editor during scene load when running in async (so main thread already has all of them)
|
||||
obj->CreateManaged();
|
||||
if (!obj->GetManagedInstance())
|
||||
obj->CreateManaged();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
||||
@@ -644,6 +644,14 @@ void Cloth::CalculateInvMasses(Array<float>& invMasses)
|
||||
int32 indicesCount;
|
||||
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
|
||||
return;
|
||||
if (_paint.Count() != verticesCount)
|
||||
{
|
||||
// Fix incorrect paint data
|
||||
int32 countBefore = _paint.Count();
|
||||
_paint.Resize(verticesCount);
|
||||
for (int32 i = countBefore; i < verticesCount; i++)
|
||||
_paint.Get()[i] = 0.0f;
|
||||
}
|
||||
const int32 verticesStride = verticesData.Length() / verticesCount;
|
||||
const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
|
||||
const int32 trianglesCount = indicesCount / 3;
|
||||
@@ -697,12 +705,12 @@ void Cloth::CalculateInvMasses(Array<float>& invMasses)
|
||||
float massSum = 0;
|
||||
for (int32 i = 0; i < verticesCount; i++)
|
||||
{
|
||||
float& mass = invMasses[i];
|
||||
float& mass = invMasses.Get()[i];
|
||||
#if USE_CLOTH_SANITY_CHECKS
|
||||
// Sanity check
|
||||
ASSERT(!isnan(mass) && !isinf(mass) && mass >= 0.0f);
|
||||
#endif
|
||||
const float maxDistance = _paint[i];
|
||||
const float maxDistance = _paint.Get()[i];
|
||||
if (maxDistance < 0.01f)
|
||||
{
|
||||
// Fixed
|
||||
@@ -722,7 +730,7 @@ void Cloth::CalculateInvMasses(Array<float>& invMasses)
|
||||
const float massScale = (float)(verticesCount - fixedCount) / massSum;
|
||||
for (int32 i = 0; i < verticesCount; i++)
|
||||
{
|
||||
float& mass = invMasses[i];
|
||||
float& mass = invMasses.Get()[i];
|
||||
if (mass > 0.0f)
|
||||
{
|
||||
mass *= massScale;
|
||||
@@ -786,6 +794,8 @@ bool Cloth::OnPreUpdate()
|
||||
auto blendWeightsStream = accessor.BlendWeights();
|
||||
if (!positionStream.IsValid() || !blendIndicesStream.IsValid() || !blendWeightsStream.IsValid())
|
||||
return false;
|
||||
if (verticesCount != _paint.Count())
|
||||
return false;
|
||||
PROFILE_CPU_NAMED("Skinned Pose");
|
||||
PhysicsBackend::LockClothParticles(_cloth);
|
||||
const Span<const Float4> particles = PhysicsBackend::GetClothParticles(_cloth);
|
||||
|
||||
@@ -272,7 +272,7 @@ bool PlatformBase::Is64BitApp()
|
||||
|
||||
int32 PlatformBase::GetCacheLineSize()
|
||||
{
|
||||
return Platform::GetCPUInfo().CacheLineSize;
|
||||
return (int32)Platform::GetCPUInfo().CacheLineSize;
|
||||
}
|
||||
|
||||
void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType error)
|
||||
|
||||
@@ -394,7 +394,7 @@ public:
|
||||
/// [Deprecated in v1.10]
|
||||
/// </summary>
|
||||
/// <returns>The cache line size.</returns>
|
||||
API_PROPERTY() DEPRECATED("Use CPUInfo.CacheLineSize instead") static int32 GetCacheLineSize();
|
||||
API_PROPERTY() DEPRECATED("Use CacheLineSize field from CPUInfo.") static int32 GetCacheLineSize();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current memory stats.
|
||||
|
||||
@@ -796,6 +796,17 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp
|
||||
// Mark as managed type
|
||||
object->Flags |= ObjectFlags::IsManagedType;
|
||||
|
||||
// Initialize managed instance
|
||||
if (params.Managed)
|
||||
{
|
||||
object->SetManagedInstance((MObject*)params.Managed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invoke managed ctor (to match C++ logic)
|
||||
object->CreateManaged();
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
@@ -412,7 +412,8 @@ void ScriptingObject::DestroyManaged()
|
||||
|
||||
void ScriptingObject::RegisterObject()
|
||||
{
|
||||
ASSERT(!IsRegistered());
|
||||
if (IsRegistered())
|
||||
return;
|
||||
Flags |= ObjectFlags::IsRegistered;
|
||||
Scripting::RegisterObject(this);
|
||||
}
|
||||
@@ -532,10 +533,6 @@ bool ManagedScriptingObject::CreateManaged()
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ensure to be registered
|
||||
if (!IsRegistered())
|
||||
RegisterObject();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -598,8 +595,7 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_Create1(MTypeObject* type)
|
||||
}
|
||||
|
||||
// Create managed object
|
||||
obj->CreateManaged();
|
||||
MObject* managedInstance = obj->GetManagedInstance();
|
||||
MObject* managedInstance = obj->GetOrCreateManagedInstance();
|
||||
if (managedInstance == nullptr)
|
||||
{
|
||||
LOG(Error, "Cannot create managed instance for type \'{0}\'.", String(typeClass->GetFullName()));
|
||||
@@ -636,8 +632,7 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_Create2(MString* typeNameObj)
|
||||
}
|
||||
|
||||
// Create managed object
|
||||
obj->CreateManaged();
|
||||
MObject* managedInstance = obj->GetManagedInstance();
|
||||
MObject* managedInstance = obj->GetOrCreateManagedInstance();
|
||||
if (managedInstance == nullptr)
|
||||
{
|
||||
LOG(Error, "Cannot create managed instance for type \'{0}\'.", String(typeName));
|
||||
@@ -667,7 +662,8 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* manage
|
||||
const ScriptingType& scriptingType = module->Types[typeIndex];
|
||||
|
||||
// Create unmanaged object
|
||||
const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
|
||||
ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
|
||||
params.Managed = managedInstance; // Link created managed instance to the unmanaged object
|
||||
ScriptingObject* obj = scriptingType.Script.Spawn(params);
|
||||
if (obj == nullptr)
|
||||
{
|
||||
@@ -675,9 +671,6 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* manage
|
||||
return;
|
||||
}
|
||||
|
||||
// Link created managed instance to the unmanaged object
|
||||
obj->SetManagedInstance(managedInstance);
|
||||
|
||||
// Set default name for actors
|
||||
if (auto* actor = dynamic_cast<Actor*>(obj))
|
||||
{
|
||||
@@ -689,8 +682,7 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* manage
|
||||
MCore::ScriptingObject::SetInternalValues(klass, managedInstance, obj, &id);
|
||||
|
||||
// Register object
|
||||
if (!obj->IsRegistered())
|
||||
obj->RegisterObject();
|
||||
obj->RegisterObject();
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceDeleted(ScriptingObject* obj)
|
||||
|
||||
@@ -355,9 +355,15 @@ struct ScriptingObjectSpawnParams
|
||||
/// </summary>
|
||||
const ScriptingTypeHandle Type;
|
||||
|
||||
/// <summary>
|
||||
/// Optional C# object instance to use for unmanaged object.
|
||||
/// </summary>
|
||||
void* Managed;
|
||||
|
||||
FORCE_INLINE ScriptingObjectSpawnParams(const Guid& id, const ScriptingTypeHandle& typeHandle)
|
||||
: ID(id)
|
||||
, Type(typeHandle)
|
||||
, Managed(nullptr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -442,7 +442,7 @@ namespace FlaxEngine.Json
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var result = Activator.CreateInstance(objectType);
|
||||
var result = existingValue ?? Activator.CreateInstance(objectType);
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
{
|
||||
JsonSerializer.ParseID((string)reader.Value, out var id);
|
||||
@@ -483,6 +483,44 @@ namespace FlaxEngine.Json
|
||||
}
|
||||
}
|
||||
|
||||
internal class ControlReferenceConverter : JsonConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override unsafe void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var id = (value as IControlReference)?.UIControl?.ID ?? Guid.Empty;
|
||||
writer.WriteValue(JsonSerializer.GetStringID(&id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteJsonDiff(JsonWriter writer, object value, object other, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
if (value is IControlReference valueRef &&
|
||||
other is IControlReference otherRef &&
|
||||
JsonSerializer.SceneObjectEquals(valueRef.UIControl, otherRef.UIControl))
|
||||
return;
|
||||
base.WriteJsonDiff(writer, value, other, serializer);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var result = existingValue ?? Activator.CreateInstance(objectType);
|
||||
if (reader.TokenType == JsonToken.String && result is IControlReference controlReference)
|
||||
{
|
||||
JsonSerializer.ParseID((string)reader.Value, out var id);
|
||||
controlReference.Load(Object.Find<UIControl>(ref id));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType.Name.StartsWith("ControlReference", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Serialize Guid values using `N` format
|
||||
|
||||
@@ -194,6 +194,7 @@ namespace FlaxEngine.Json
|
||||
settings.Converters.Add(new SoftObjectReferenceConverter());
|
||||
settings.Converters.Add(new SoftTypeReferenceConverter());
|
||||
settings.Converters.Add(new BehaviorKnowledgeSelectorAnyConverter());
|
||||
settings.Converters.Add(new ControlReferenceConverter());
|
||||
settings.Converters.Add(new MarginConverter());
|
||||
settings.Converters.Add(new VersionConverter());
|
||||
settings.Converters.Add(new LocalizedStringConverter());
|
||||
@@ -227,6 +228,23 @@ namespace FlaxEngine.Json
|
||||
CacheManagedOnly.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the values comparision function used by the serialization system.
|
||||
/// </summary>
|
||||
/// <param name="objA">The object a.</param>
|
||||
/// <param name="objB">The object b.</param>
|
||||
/// <returns>True if both objects are equal, otherwise false.</returns>
|
||||
public static bool SceneObjectEquals(SceneObject objA, SceneObject objB)
|
||||
{
|
||||
if (objA == objB)
|
||||
return true;
|
||||
if (objA == null || objB == null)
|
||||
return false;
|
||||
if (objA.HasPrefabLink && objB.HasPrefabLink)
|
||||
return objA.PrefabObjectID == objB.PrefabObjectID;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the values comparision function used by the serialization system.
|
||||
/// </summary>
|
||||
|
||||
@@ -74,13 +74,13 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z)
|
||||
_physicsHeightField = nullptr;
|
||||
_x = x;
|
||||
_z = z;
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::ChunksCountEdge;
|
||||
_offset = Float3(_x * size, 0.0f, _z * size);
|
||||
_yOffset = 0.0f;
|
||||
_yHeight = 1.0f;
|
||||
for (int32 i = 0; i < Terrain::ChunksCount; i++)
|
||||
{
|
||||
Chunks[i].Init(this, i % Terrain::Terrain::ChunksCountEdge, i / Terrain::Terrain::ChunksCountEdge);
|
||||
Chunks[i].Init(this, i % Terrain::ChunksCountEdge, i / Terrain::ChunksCountEdge);
|
||||
}
|
||||
Heightmap = nullptr;
|
||||
for (int32 i = 0; i < TERRAIN_MAX_SPLATMAPS_COUNT; i++)
|
||||
@@ -99,7 +99,7 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z)
|
||||
}
|
||||
#endif
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
SAFE_DELETE(_debugLines);
|
||||
SAFE_DELETE_GPU_RESOURCE(_debugLines);
|
||||
_debugLinesDirty = true;
|
||||
#endif
|
||||
#if USE_EDITOR
|
||||
@@ -117,6 +117,9 @@ TerrainPatch::~TerrainPatch()
|
||||
SAFE_DELETE(_dataSplatmap[i]);
|
||||
}
|
||||
#endif
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
SAFE_DELETE_GPU_RESOURCE(_debugLines);
|
||||
#endif
|
||||
}
|
||||
|
||||
RawDataAsset* TerrainPatch::GetHeightfield() const
|
||||
@@ -2324,9 +2327,9 @@ void TerrainPatch::DrawPhysicsDebug(RenderView& view)
|
||||
{
|
||||
if (!_debugLines || _debugLinesDirty)
|
||||
CacheDebugLines();
|
||||
const Transform terrainTransform = _terrain->_transform;
|
||||
const Transform localTransform(Vector3(0, _yOffset, 0), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
|
||||
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::ChunksCountEdge;
|
||||
const Transform localTransform(Vector3(_x * size, _yOffset, _z * size), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
|
||||
const Matrix world = localTransform.GetWorld() * _terrain->_transform.GetWorld();
|
||||
DebugDraw::DrawLines(_debugLines, world);
|
||||
}
|
||||
}
|
||||
@@ -2352,10 +2355,9 @@ const Array<Vector3>& TerrainPatch::GetCollisionTriangles()
|
||||
|
||||
#define GET_VERTEX(x, y) Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))); Vector3::Transform(v##x##y, world, v##x##y)
|
||||
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
|
||||
const Transform terrainTransform = _terrain->_transform;
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::ChunksCountEdge;
|
||||
Transform localTransform(Vector3(_x * size, _yOffset, _z * size), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
|
||||
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
|
||||
const Matrix world = localTransform.GetWorld() * _terrain->_transform.GetWorld();
|
||||
|
||||
for (int32 row = 0; row < rows - 1; row++)
|
||||
{
|
||||
@@ -2401,7 +2403,7 @@ void TerrainPatch::GetCollisionTriangles(const BoundingSphere& bounds, Array<Vec
|
||||
|
||||
// Prepare
|
||||
const auto& triangles = GetCollisionTriangles();
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::ChunksCountEdge;
|
||||
Transform transform;
|
||||
transform.Translation = _offset + Vector3(0, _yOffset, 0);
|
||||
transform.Orientation = Quaternion::Identity;
|
||||
@@ -2507,10 +2509,9 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Float3>& vertexBuffer, Array<i
|
||||
ScopeLock sceneLock(Level::ScenesLock);
|
||||
if (_collisionVertices.IsEmpty())
|
||||
{
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::Terrain::ChunksCountEdge;
|
||||
const Transform terrainTransform = _terrain->_transform;
|
||||
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * Terrain::ChunksCountEdge;
|
||||
const Transform localTransform(Vector3(_x * size, _yOffset, _z * size), Quaternion::Identity, Float3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
|
||||
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
|
||||
const Matrix world = localTransform.GetWorld() * _terrain->_transform.GetWorld();
|
||||
|
||||
const int32 vertexCount = rows * cols;
|
||||
_collisionVertices.Resize(vertexCount);
|
||||
@@ -2569,7 +2570,7 @@ void TerrainPatch::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
|
||||
stream.JKEY("Chunks");
|
||||
stream.StartArray();
|
||||
for (int32 i = 0; i < Terrain::Terrain::ChunksCount; i++)
|
||||
for (int32 i = 0; i < Terrain::ChunksCount; i++)
|
||||
{
|
||||
stream.StartObject();
|
||||
Chunks[i].Serialize(stream, other ? &other->Chunks[i] : nullptr);
|
||||
|
||||
199
Source/Engine/UI/ControlReference.cs
Normal file
199
Source/Engine/UI/ControlReference.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for control references access.
|
||||
/// </summary>
|
||||
public interface IControlReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the reference to <see cref="FlaxEngine.UIControl"/> actor.
|
||||
/// </summary>
|
||||
public UIControl UIControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the control the interface uses.
|
||||
/// </summary>
|
||||
public Type ControlType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets control ref by force - used during loading when <see cref="UIControl.Control"/> is not loaded yet.
|
||||
/// </summary>
|
||||
/// <param name="control">The reference.</param>
|
||||
internal void Load(UIControl control);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI Control reference utility. References UI Control actor with a typed control type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the UI control object.</typeparam>
|
||||
#if FLAX_EDITOR
|
||||
[TypeConverter(typeof(TypeConverters.ControlReferenceConverter))]
|
||||
#endif
|
||||
public struct ControlReference<T> : IControlReference, IComparable, IComparable<ControlReference<T>>, IEquatable<ControlReference<T>> where T : Control
|
||||
{
|
||||
private UIControl _uiControl;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the typed UI control object owned by the referenced <see cref="FlaxEngine.UIControl"/> actor.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public T Control
|
||||
{
|
||||
get
|
||||
{
|
||||
var control = _uiControl?.Control;
|
||||
if (control == null)
|
||||
return null;
|
||||
if (control is T t)
|
||||
return t;
|
||||
Debug.Write(LogType.Warning, $"Trying to get Control from ControlReference but UIControl.Control is not correct type. It should be {typeof(T)} but is {control.GetType()}.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public UIControl UIControl
|
||||
{
|
||||
get => _uiControl;
|
||||
set
|
||||
{
|
||||
var control = value?.Control;
|
||||
if (value == null)
|
||||
{
|
||||
_uiControl = null;
|
||||
}
|
||||
else if (control is T)
|
||||
{
|
||||
_uiControl = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Write(LogType.Warning, $"Trying to set UIControl but UIControl.Control is not the correct type. It should be {typeof(T)} but is {control.GetType()}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type ControlType => typeof(T);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load(UIControl value)
|
||||
{
|
||||
_uiControl = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return _uiControl?.ToString() ?? "null";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _uiControl?.GetHashCode() ?? 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is IControlReference other)
|
||||
return Json.JsonSerializer.SceneObjectEquals(_uiControl, other.UIControl) ? 0 : 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(ControlReference<T> other)
|
||||
{
|
||||
return Json.JsonSerializer.SceneObjectEquals(_uiControl, other._uiControl) ? 0 : 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(ControlReference<T> other)
|
||||
{
|
||||
return Json.JsonSerializer.SceneObjectEquals(_uiControl, other._uiControl);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ControlReference<T> other && Json.JsonSerializer.SceneObjectEquals(_uiControl, other._uiControl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The implicit operator for the Control.
|
||||
/// </summary>
|
||||
/// <param name="reference">The control reference.</param>
|
||||
/// <returns>The control object.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator T(ControlReference<T> reference) => reference.Control;
|
||||
|
||||
/// <summary>
|
||||
/// The implicit operator for the UIControl.
|
||||
/// </summary>
|
||||
/// <param name="reference">The control reference.</param>
|
||||
/// <returns>The control actor.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator UIControl(ControlReference<T> reference) => reference.UIControl;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the object exists (reference is not null and the unmanaged object pointer is valid).
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to check.</param>
|
||||
/// <returns>True if object is valid, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator bool(ControlReference<T> obj) => obj._uiControl;
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two objects are equal.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(ControlReference<T> left, ControlReference<T> right) => Json.JsonSerializer.SceneObjectEquals(left._uiControl, right._uiControl);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two objects are not equal.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(ControlReference<T> left, ControlReference<T> right) => !Json.JsonSerializer.SceneObjectEquals(left._uiControl, right._uiControl);
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class ControlReferenceConverter : TypeConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (value is string valueStr)
|
||||
{
|
||||
var result = Activator.CreateInstance(destinationType);
|
||||
if (result is IControlReference control)
|
||||
{
|
||||
Json.JsonSerializer.ParseID(valueStr, out var id);
|
||||
control.Load(Object.Find<UIControl>(ref id));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType.Name.StartsWith("ControlReference", StringComparison.Ordinal))
|
||||
return true;
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -35,7 +35,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// The cells heights in container height percentage (from top to bottom). Use negative values to set fixed widths for the cells.
|
||||
/// </summary>
|
||||
[EditorOrder(10), Tooltip("The cells heights in container height percentage (from top to bottom). Use negative values to set fixed height for the cells.")]
|
||||
[EditorOrder(10), Limit(float.MinValue, float.MaxValue, 0.001f), Tooltip("The cells heights in container height percentage (from top to bottom). Use negative values to set fixed height for the cells.")]
|
||||
public float[] RowFill
|
||||
{
|
||||
get => _cellsV;
|
||||
@@ -49,7 +49,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// The cells heights in container width percentage (from left to right). Use negative values to set fixed heights for the cells.
|
||||
/// </summary>
|
||||
[EditorOrder(20), Tooltip("The cells heights in container width percentage (from left to right). Use negative values to set fixed width for the cells.")]
|
||||
[EditorOrder(20), Limit(float.MinValue, float.MaxValue, 0.001f), Tooltip("The cells heights in container width percentage (from left to right). Use negative values to set fixed width for the cells.")]
|
||||
public float[] ColumnFill
|
||||
{
|
||||
get => _cellsH;
|
||||
|
||||
Reference in New Issue
Block a user