Merge branch 'master' into 1.2
# Conflicts: # Source/Engine/Platform/Network.h
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
"Name": "Flax",
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 0,
|
||||
"Build": 6216
|
||||
"Minor": 1,
|
||||
"Build": 6217
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -250,6 +250,7 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
Selection.Clear();
|
||||
Selection.Add(obj);
|
||||
Selection.SetType(new ScriptType(obj.GetType()));
|
||||
|
||||
OnSelectionChanged();
|
||||
}
|
||||
@@ -271,6 +272,7 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
Selection.Clear();
|
||||
Selection.AddRange(objectsArray);
|
||||
Selection.SetType(new ScriptType(objectsArray.GetType()));
|
||||
|
||||
OnSelectionChanged();
|
||||
}
|
||||
@@ -284,6 +286,7 @@ namespace FlaxEditor.CustomEditors
|
||||
return;
|
||||
|
||||
Selection.Clear();
|
||||
Selection.SetType(ScriptType.Null);
|
||||
|
||||
OnSelectionChanged();
|
||||
}
|
||||
|
||||
@@ -22,10 +22,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
element = layout.ComboBox();
|
||||
element.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
|
||||
// Set layer names
|
||||
element.ComboBox.SetItems(LayersAndTagsSettings.GetCurrentLayers());
|
||||
element.ComboBox.SelectedIndex = (int)Values[0];
|
||||
element.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
}
|
||||
|
||||
private void GetActorsTree(List<Actor> list, Actor a)
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace FlaxEditor.CustomEditors
|
||||
/// <summary>
|
||||
/// Gets the values type.
|
||||
/// </summary>
|
||||
public ScriptType Type { get; }
|
||||
public ScriptType Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether single object is selected.
|
||||
@@ -167,14 +167,14 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
if (_hasReferenceValue)
|
||||
{
|
||||
if (_referenceValue is SceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink)
|
||||
if (_referenceValue is SceneObject referenceSceneObject && referenceSceneObject && referenceSceneObject.HasPrefabLink)
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i] == referenceSceneObject)
|
||||
continue;
|
||||
|
||||
if (this[i] == null || (this[i] is SceneObject valueSceneObject && valueSceneObject.PrefabObjectID != referenceSceneObject.PrefabObjectID))
|
||||
if (this[i] == null || (this[i] is SceneObject valueSceneObject && valueSceneObject && valueSceneObject.PrefabObjectID != referenceSceneObject.PrefabObjectID))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -251,6 +251,10 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
if (instanceValues._hasReferenceValue)
|
||||
{
|
||||
// If the reference value is set for the parent values but it's null object then skip it
|
||||
if (instanceValues._referenceValue == null && !instanceValues.Type.IsValueType)
|
||||
return;
|
||||
|
||||
_referenceValue = Info.GetValue(instanceValues._referenceValue);
|
||||
_hasReferenceValue = true;
|
||||
}
|
||||
@@ -280,6 +284,15 @@ namespace FlaxEditor.CustomEditors
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type. Use with caution.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
public void SetType(ScriptType type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom attributes defined for the values source member.
|
||||
/// </summary>
|
||||
|
||||
@@ -816,9 +816,16 @@ namespace FlaxEditor
|
||||
/// <param name="sphere">The bounding sphere.</param>
|
||||
public static void GetActorEditorSphere(Actor actor, out BoundingSphere sphere)
|
||||
{
|
||||
Internal_GetEditorBoxWithChildren(FlaxEngine.Object.GetUnmanagedPtr(actor), out var box);
|
||||
BoundingSphere.FromBox(ref box, out sphere);
|
||||
sphere.Radius = Math.Max(sphere.Radius, 15.0f);
|
||||
if (actor)
|
||||
{
|
||||
Internal_GetEditorBoxWithChildren(FlaxEngine.Object.GetUnmanagedPtr(actor), out var box);
|
||||
BoundingSphere.FromBox(ref box, out sphere);
|
||||
sphere.Radius = Math.Max(sphere.Radius, 15.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
sphere = BoundingSphere.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -828,7 +835,14 @@ namespace FlaxEditor
|
||||
/// <param name="box">The bounding box.</param>
|
||||
public static void GetActorEditorBox(Actor actor, out BoundingBox box)
|
||||
{
|
||||
Internal_GetEditorBoxWithChildren(FlaxEngine.Object.GetUnmanagedPtr(actor), out box);
|
||||
if (actor)
|
||||
{
|
||||
Internal_GetEditorBoxWithChildren(FlaxEngine.Object.GetUnmanagedPtr(actor), out box);
|
||||
}
|
||||
else
|
||||
{
|
||||
box = BoundingBox.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1166,7 +1180,7 @@ namespace FlaxEditor
|
||||
if (Windows.GameWin != null && Windows.GameWin.ContainsFocus)
|
||||
{
|
||||
var win = Windows.GameWin.Root;
|
||||
if (win != null && win.RootWindow is WindowRootControl root && root.Window.IsFocused)
|
||||
if (win?.RootWindow is WindowRootControl root && root.Window && root.Window.IsFocused)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1179,7 +1193,7 @@ namespace FlaxEditor
|
||||
if (Windows.GameWin != null && Windows.GameWin.ContainsFocus)
|
||||
{
|
||||
var win = Windows.GameWin.Root;
|
||||
if (win != null && win.RootWindow is WindowRootControl root && root.Window.IsFocused)
|
||||
if (win?.RootWindow is WindowRootControl root && root.Window && root.Window.IsFocused)
|
||||
{
|
||||
pos = Vector2.Round(Windows.GameWin.Viewport.PointFromScreen(pos) * root.DpiScale);
|
||||
}
|
||||
@@ -1199,7 +1213,7 @@ namespace FlaxEditor
|
||||
if (Windows.GameWin != null && Windows.GameWin.ContainsFocus)
|
||||
{
|
||||
var win = Windows.GameWin.Root;
|
||||
if (win != null && win.RootWindow is WindowRootControl root && root.Window.IsFocused)
|
||||
if (win?.RootWindow is WindowRootControl root && root.Window && root.Window.IsFocused)
|
||||
{
|
||||
pos = Vector2.Round(Windows.GameWin.Viewport.PointToScreen(pos / root.DpiScale));
|
||||
}
|
||||
|
||||
@@ -47,6 +47,11 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
private Window _window;
|
||||
private Control _previouslyFocused;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether use automatic popup direction fix based on the screen dimensions.
|
||||
/// </summary>
|
||||
protected virtual bool UseAutomaticDirectionFix => true;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if context menu is opened
|
||||
/// </summary>
|
||||
@@ -132,21 +137,24 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
Rectangle monitorBounds = Platform.GetMonitorBounds(locationSS);
|
||||
Vector2 rightBottomLocationSS = locationSS + dpiSize;
|
||||
bool isUp = false, isLeft = false;
|
||||
if (monitorBounds.Bottom < rightBottomLocationSS.Y)
|
||||
if (UseAutomaticDirectionFix)
|
||||
{
|
||||
// Direction: up
|
||||
isUp = true;
|
||||
locationSS.Y -= dpiSize.Y;
|
||||
if (monitorBounds.Bottom < rightBottomLocationSS.Y)
|
||||
{
|
||||
// Direction: up
|
||||
isUp = true;
|
||||
locationSS.Y -= dpiSize.Y;
|
||||
|
||||
// Offset to fix sub-menu location
|
||||
if (parent is ContextMenu menu && menu._childCM != null)
|
||||
locationSS.Y += 30.0f * dpiScale;
|
||||
}
|
||||
if (monitorBounds.Right < rightBottomLocationSS.X)
|
||||
{
|
||||
// Direction: left
|
||||
isLeft = true;
|
||||
locationSS.X -= dpiSize.X;
|
||||
// Offset to fix sub-menu location
|
||||
if (parent is ContextMenu menu && menu._childCM != null)
|
||||
locationSS.Y += 30.0f * dpiScale;
|
||||
}
|
||||
if (monitorBounds.Right < rightBottomLocationSS.X)
|
||||
{
|
||||
// Direction: left
|
||||
isLeft = true;
|
||||
locationSS.X -= dpiSize.X;
|
||||
}
|
||||
}
|
||||
|
||||
// Update direction flag
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
throw new InvalidOperationException("Missing parent window.");
|
||||
var control = _tabsProxy != null ? (Control)_tabsProxy : this;
|
||||
var clientPos = control.PointToWindow(Vector2.Zero);
|
||||
return new Rectangle(parentWin.PointToScreen(clientPos), control.Size * RootWindow.DpiScale);
|
||||
return new Rectangle(parentWin.PointToScreen(clientPos), control.Size * DpiScale);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,9 +42,6 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the initial value.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The initial value.
|
||||
/// </value>
|
||||
public string InitialValue
|
||||
{
|
||||
get => _startValue;
|
||||
@@ -54,9 +51,6 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the input field text.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The text.
|
||||
/// </value>
|
||||
public string Text
|
||||
{
|
||||
get => _inputField.Text;
|
||||
@@ -138,6 +132,9 @@ namespace FlaxEditor.GUI
|
||||
Hide();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool UseAutomaticDirectionFix => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
|
||||
@@ -1438,6 +1438,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
ArrangeTracks();
|
||||
PerformLayout(true);
|
||||
UnlockChildrenRecursive();
|
||||
PerformLayout(true);
|
||||
Profiler.EndEvent();
|
||||
|
||||
ClearEditedFlag();
|
||||
|
||||
@@ -50,6 +50,41 @@ namespace FlaxEditor.Gizmo
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function, recursively finds the Prefab Root of node or null.
|
||||
/// </summary>
|
||||
/// <param name="node">The node from which to start.</param>
|
||||
/// <returns>The prefab root or null.</returns>
|
||||
public ActorNode GetPrefabRootInParent(ActorNode node)
|
||||
{
|
||||
if (!node.HasPrefabLink)
|
||||
return null;
|
||||
if (node.Actor.IsPrefabRoot)
|
||||
return node;
|
||||
if (node.ParentNode is ActorNode parAct)
|
||||
return GetPrefabRootInParent(parAct);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively walks up from the node up to ceiling node(inclusive) or selection(exclusive).
|
||||
/// </summary>
|
||||
/// <param name="node">The node from which to start</param>
|
||||
/// <param name="ceiling">The ceiling(inclusive)</param>
|
||||
/// <returns>The node to select.</returns>
|
||||
public ActorNode WalkUpAndFindActorNodeBeforeSelection(ActorNode node, ActorNode ceiling)
|
||||
{
|
||||
if (node == ceiling || _selection.Contains(node))
|
||||
return node;
|
||||
if (node.ParentNode is ActorNode parentNode)
|
||||
{
|
||||
if (_selection.Contains(node.ParentNode))
|
||||
return node;
|
||||
return WalkUpAndFindActorNodeBeforeSelection(parentNode, ceiling);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Pick()
|
||||
{
|
||||
@@ -100,6 +135,16 @@ namespace FlaxEditor.Gizmo
|
||||
}
|
||||
}
|
||||
|
||||
// Select prefab root and then go down until you find the actual item in which case select the prefab root again
|
||||
if (hit is ActorNode actorNode)
|
||||
{
|
||||
ActorNode prefabRoot = GetPrefabRootInParent(actorNode);
|
||||
if (prefabRoot != null && actorNode != prefabRoot)
|
||||
{
|
||||
hit = WalkUpAndFindActorNodeBeforeSelection(actorNode, prefabRoot);
|
||||
}
|
||||
}
|
||||
|
||||
bool addRemove = Owner.IsControlDown;
|
||||
bool isSelected = sceneEditing.Selection.Contains(hit);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace FlaxEditor.Gizmo
|
||||
// Return arithmetic average or whatever it means
|
||||
return center / count;
|
||||
}
|
||||
|
||||
|
||||
private bool IntersectsRotateCircle(Vector3 normal, ref Ray ray, out float distance)
|
||||
{
|
||||
var plane = new Plane(Vector3.Zero, normal);
|
||||
@@ -51,7 +51,7 @@ namespace FlaxEditor.Gizmo
|
||||
Vector3.Transform(ref ray.Position, ref invGizmoWorld, out localRay.Position);
|
||||
|
||||
// Find gizmo collisions with mouse
|
||||
float closestintersection = float.MaxValue;
|
||||
float closestIntersection = float.MaxValue;
|
||||
float intersection;
|
||||
_activeAxis = Axis.None;
|
||||
switch (_activeMode)
|
||||
@@ -59,42 +59,42 @@ namespace FlaxEditor.Gizmo
|
||||
case Mode.Translate:
|
||||
{
|
||||
// Axis boxes collision
|
||||
if (XAxisBox.Intersects(ref localRay, out intersection) && intersection < closestintersection)
|
||||
if (XAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.X;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
|
||||
if (YAxisBox.Intersects(ref localRay, out intersection) && intersection < closestintersection)
|
||||
if (YAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.Y;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
|
||||
if (ZAxisBox.Intersects(ref localRay, out intersection) && intersection < closestintersection)
|
||||
if (ZAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.Z;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
|
||||
// Quad planes collision
|
||||
if (closestintersection >= float.MaxValue)
|
||||
closestintersection = float.MinValue;
|
||||
if (XYBox.Intersects(ref localRay, out intersection) && intersection > closestintersection)
|
||||
if (closestIntersection >= float.MaxValue)
|
||||
closestIntersection = float.MinValue;
|
||||
if (XYBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.XY;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
|
||||
if (XZBox.Intersects(ref localRay, out intersection) && intersection > closestintersection)
|
||||
if (XZBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.ZX;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
if (YZBox.Intersects(ref localRay, out intersection) && intersection > closestintersection)
|
||||
if (YZBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.YZ;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -103,20 +103,20 @@ namespace FlaxEditor.Gizmo
|
||||
case Mode.Rotate:
|
||||
{
|
||||
// Circles
|
||||
if (IntersectsRotateCircle(Vector3.UnitX, ref localRay, out intersection) && intersection < closestintersection)
|
||||
if (IntersectsRotateCircle(Vector3.UnitX, ref localRay, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.X;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
if (IntersectsRotateCircle(Vector3.UnitY, ref localRay, out intersection) && intersection < closestintersection)
|
||||
if (IntersectsRotateCircle(Vector3.UnitY, ref localRay, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.Y;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
if (IntersectsRotateCircle(Vector3.UnitZ, ref localRay, out intersection) && intersection < closestintersection)
|
||||
if (IntersectsRotateCircle(Vector3.UnitZ, ref localRay, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.Z;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
|
||||
// Center
|
||||
@@ -132,27 +132,27 @@ namespace FlaxEditor.Gizmo
|
||||
case Mode.Scale:
|
||||
{
|
||||
// Spheres collision
|
||||
if (ScaleXSphere.Intersects(ref ray, out intersection) && intersection < closestintersection)
|
||||
if (ScaleXSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.X;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
if (ScaleYSphere.Intersects(ref ray, out intersection) && intersection < closestintersection)
|
||||
if (ScaleYSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.Y;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
if (ScaleZSphere.Intersects(ref ray, out intersection) && intersection < closestintersection)
|
||||
if (ScaleZSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.Z;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
|
||||
// Center
|
||||
if (CenterBox.Intersects(ref ray, out intersection) && intersection < closestintersection)
|
||||
if (CenterBox.Intersects(ref ray, out intersection) && intersection < closestIntersection)
|
||||
{
|
||||
_activeAxis = Axis.Center;
|
||||
closestintersection = intersection;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace FlaxEditor.Gizmo
|
||||
_axisAlignedWorld = _screenScaleMatrix * Matrix.CreateWorld(Position, Vector3.Backward, Vector3.Up);
|
||||
|
||||
// Assign world
|
||||
if (_activeTransformSpace == TransformSpace.World)
|
||||
if (_activeTransformSpace == TransformSpace.World && _activeMode != Mode.Scale)
|
||||
{
|
||||
_gizmoWorld = _axisAlignedWorld;
|
||||
|
||||
@@ -297,29 +297,6 @@ namespace FlaxEditor.Gizmo
|
||||
else if (_activeMode == Mode.Scale)
|
||||
{
|
||||
// Scale
|
||||
if (_activeTransformSpace == TransformSpace.World && _activeAxis != Axis.Center)
|
||||
{
|
||||
var deltaLocal = delta;
|
||||
Quaternion orientation = GetSelectedObject(0).Orientation;
|
||||
delta = Vector3.Transform(delta, orientation);
|
||||
|
||||
// Fix axis sign of delta movement for rotated object in some cases (eg. rotated object by 90 deg on Y axis and scale in world space with Red/X axis)
|
||||
switch (_activeAxis)
|
||||
{
|
||||
case Axis.X:
|
||||
if (deltaLocal.X < 0)
|
||||
delta *= -1;
|
||||
break;
|
||||
case Axis.Y:
|
||||
if (deltaLocal.Y < 0)
|
||||
delta *= -1;
|
||||
break;
|
||||
case Axis.Z:
|
||||
if (deltaLocal.Z < 0)
|
||||
delta *= -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_scaleDelta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +263,12 @@ namespace FlaxEditor.SceneGraph
|
||||
return _actor.IntersectsItself(ray.Ray, out distance, out normal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetEditorSphere(out BoundingSphere sphere)
|
||||
{
|
||||
Editor.GetActorEditorSphere(_actor, out sphere);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDebugDraw(ViewportDebugDrawData data)
|
||||
{
|
||||
@@ -315,5 +321,11 @@ namespace FlaxEditor.SceneGraph
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return _actor ? _actor.ToString() : base.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,6 +309,20 @@ namespace FlaxEditor.SceneGraph
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object bounding sphere (including child actors).
|
||||
/// </summary>
|
||||
/// <param name="sphere">The bounding sphere.</param>
|
||||
public virtual void GetEditorSphere(out BoundingSphere sphere)
|
||||
{
|
||||
sphere = new BoundingSphere(Transform.Translation, 15.0f);
|
||||
for (int i = 0; i < ChildNodes.Count; i++)
|
||||
{
|
||||
ChildNodes[i].GetEditorSphere(out var childSphere);
|
||||
BoundingSphere.Merge(ref sphere, ref childSphere, out sphere);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when selected nodes should draw debug shapes using <see cref="DebugDraw"/> interface.
|
||||
/// </summary>
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
{
|
||||
_resultPanel.DisposeChildren();
|
||||
|
||||
var dpiScale = RootWindow.DpiScale;
|
||||
var dpiScale = DpiScale;
|
||||
|
||||
if (items.Count == 0)
|
||||
{
|
||||
|
||||
@@ -578,6 +578,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
BreakConnection(connectedBox);
|
||||
action.End();
|
||||
Surface.Undo.AddAction(action);
|
||||
Surface.MarkAsEdited();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -298,7 +298,6 @@ namespace FlaxEditor.Surface
|
||||
rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox);
|
||||
addConnectionsAction.End();
|
||||
|
||||
|
||||
Undo.AddAction(new MultiUndoAction(spawnNodeAction, disconnectBoxesAction, addConnectionsAction));
|
||||
}
|
||||
else
|
||||
@@ -565,11 +564,10 @@ namespace FlaxEditor.Surface
|
||||
if (key == KeyboardKeys.ArrowUp || key == KeyboardKeys.ArrowDown)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
if (selectedBox == null)
|
||||
return true;
|
||||
|
||||
Box toSelect = (key == KeyboardKeys.ArrowUp) ?
|
||||
selectedBox?.ParentNode.GetPreviousBox(selectedBox) :
|
||||
selectedBox?.ParentNode.GetNextBox(selectedBox);
|
||||
Box toSelect = (key == KeyboardKeys.ArrowUp) ? selectedBox?.ParentNode.GetPreviousBox(selectedBox) : selectedBox?.ParentNode.GetNextBox(selectedBox);
|
||||
|
||||
if (toSelect != null && toSelect.IsOutput == selectedBox.IsOutput)
|
||||
{
|
||||
@@ -581,10 +579,12 @@ namespace FlaxEditor.Surface
|
||||
if (key == KeyboardKeys.Tab)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
if (selectedBox == null)
|
||||
return true;
|
||||
|
||||
int connectionCount = selectedBox.Connections.Count;
|
||||
if (connectionCount == 0) return true;
|
||||
if (connectionCount == 0)
|
||||
return true;
|
||||
|
||||
if (Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
@@ -596,11 +596,11 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (key == KeyboardKeys.ArrowRight || key == KeyboardKeys.ArrowLeft)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
if (selectedBox == null)
|
||||
return true;
|
||||
|
||||
Box toSelect = null;
|
||||
|
||||
@@ -633,7 +633,6 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
Select(toSelect.ParentNode);
|
||||
toSelect.ParentNode.SelectBox(toSelect);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -825,10 +824,7 @@ namespace FlaxEditor.Surface
|
||||
xLocation += -120 - distanceBetweenNodes.X;
|
||||
}
|
||||
|
||||
return new Vector2(
|
||||
xLocation,
|
||||
yLocation
|
||||
);
|
||||
return new Vector2(xLocation, yLocation);
|
||||
}
|
||||
|
||||
private bool IntersectsConnection(Vector2 mousePosition, out InputBox inputBox, out OutputBox outputBox)
|
||||
|
||||
@@ -94,6 +94,15 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
Viewport.ViewPosition = _orbitCenter + localPosition;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius)
|
||||
{
|
||||
base.SetArcBallView(orientation, orbitCenter, orbitRadius);
|
||||
|
||||
_orbitCenter = orbitCenter;
|
||||
_orbitRadius = orbitRadius;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UpdateView(float dt, ref Vector3 moveDelta, ref Vector2 mouseDelta, out bool centerMouse)
|
||||
{
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
Editor.GetActorEditorSphere(actor, out BoundingSphere sphere);
|
||||
ShowSphere(ref sphere);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Moves the viewport to visualize selected actors.
|
||||
/// </summary>
|
||||
@@ -103,48 +103,46 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
/// <summary>
|
||||
/// Moves the viewport to visualize selected actors.
|
||||
/// </summary>
|
||||
/// <param name="actors">The actors to show.</param>
|
||||
public void ShowActors(List<SceneGraphNode> actors)
|
||||
/// <param name="selection">The actors to show.</param>
|
||||
public void ShowActors(List<SceneGraphNode> selection)
|
||||
{
|
||||
if (actors.Count == 0)
|
||||
if (selection.Count == 0)
|
||||
return;
|
||||
|
||||
BoundingSphere mergesSphere = BoundingSphere.Empty;
|
||||
for (int i = 0; i < actors.Count; i++)
|
||||
for (int i = 0; i < selection.Count; i++)
|
||||
{
|
||||
if (actors[i] is ActorNode actor)
|
||||
{
|
||||
Editor.GetActorEditorSphere(actor.Actor, out BoundingSphere sphere);
|
||||
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
|
||||
}
|
||||
selection[i].GetEditorSphere(out var sphere);
|
||||
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
|
||||
}
|
||||
|
||||
if (mergesSphere == BoundingSphere.Empty)
|
||||
return;
|
||||
ShowSphere(ref mergesSphere);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the viewport to visualize selected actors.
|
||||
/// </summary>
|
||||
/// <param name="actors">The actors to show.</param>
|
||||
/// <param name="selection">The actors to show.</param>
|
||||
/// <param name="orientation">The used orientation.</param>
|
||||
public void ShowActors(List<SceneGraphNode> actors, ref Quaternion orientation)
|
||||
public void ShowActors(List<SceneGraphNode> selection, ref Quaternion orientation)
|
||||
{
|
||||
if (actors.Count == 0)
|
||||
if (selection.Count == 0)
|
||||
return;
|
||||
|
||||
BoundingSphere mergesSphere = BoundingSphere.Empty;
|
||||
for (int i = 0; i < actors.Count; i++)
|
||||
for (int i = 0; i < selection.Count; i++)
|
||||
{
|
||||
if (actors[i] is ActorNode actor)
|
||||
{
|
||||
Editor.GetActorEditorSphere(actor.Actor, out BoundingSphere sphere);
|
||||
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
|
||||
}
|
||||
selection[i].GetEditorSphere(out var sphere);
|
||||
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
|
||||
}
|
||||
|
||||
if (mergesSphere == BoundingSphere.Empty)
|
||||
return;
|
||||
ShowSphere(ref mergesSphere, ref orientation);
|
||||
}
|
||||
|
||||
|
||||
private void ShowSphere(ref BoundingSphere sphere)
|
||||
{
|
||||
var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f);
|
||||
@@ -154,18 +152,27 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
private void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation)
|
||||
{
|
||||
Vector3 position;
|
||||
|
||||
if (Viewport.UseOrthographicProjection)
|
||||
{
|
||||
position = sphere.Center + Vector3.Backward * orientation * (sphere.Radius * 5.0f);
|
||||
Viewport.OrthographicScale = Vector3.Distance(position, sphere.Center) / 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = sphere.Center - Vector3.Forward * orientation * (sphere.Radius * 2.5f);
|
||||
TargetPoint = position;
|
||||
}
|
||||
TargetPoint = sphere.Center;
|
||||
MoveViewport(position, orientation);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius)
|
||||
{
|
||||
base.SetArcBallView(orientation, orbitCenter, orbitRadius);
|
||||
|
||||
TargetPoint = orbitCenter;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
@@ -204,12 +211,12 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
|
||||
Viewport.GetInput(out var input);
|
||||
Viewport.GetPrevInput(out var prevInput);
|
||||
var mainViewport = Viewport as MainEditorGizmoViewport;
|
||||
bool isUsingGizmo = mainViewport != null && mainViewport.TransformGizmo.ActiveAxis != TransformGizmoBase.Axis.None;
|
||||
var transformGizmo = (Viewport as EditorGizmoViewport)?.Gizmos.Active as TransformGizmoBase;
|
||||
var isUsingGizmo = transformGizmo != null && transformGizmo.ActiveAxis != TransformGizmoBase.Axis.None;
|
||||
|
||||
// Get current view properties
|
||||
float yaw = Viewport.Yaw;
|
||||
float pitch = Viewport.Pitch;
|
||||
var yaw = Viewport.Yaw;
|
||||
var pitch = Viewport.Pitch;
|
||||
var position = Viewport.ViewPosition;
|
||||
var rotation = Viewport.ViewOrientation;
|
||||
|
||||
@@ -263,7 +270,7 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
position += forward * (Viewport.MouseWheelZoomSpeedFactor * input.MouseWheelDelta * 25.0f);
|
||||
if (input.IsAltDown)
|
||||
{
|
||||
position += forward * (Viewport.MouseSpeed * 40 * Viewport.MouseDeltaRight.ValuesSum);
|
||||
position += forward * (Viewport.MouseSpeed * 40 * Viewport.MousePositionDelta.ValuesSum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +278,7 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
if (input.IsOrbiting && isUsingGizmo)
|
||||
{
|
||||
centerMouse = false;
|
||||
Viewport.ViewPosition += mainViewport.TransformGizmo.LastDelta.Translation;
|
||||
Viewport.ViewPosition += transformGizmo.LastDelta.Translation;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -280,7 +287,7 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
Viewport.Pitch = pitch;
|
||||
if (input.IsOrbiting)
|
||||
{
|
||||
float orbitRadius = Vector3.Distance(ref position, ref TargetPoint);
|
||||
float orbitRadius = Mathf.Max(Vector3.Distance(ref position, ref TargetPoint), 0.0001f);
|
||||
Vector3 localPosition = Viewport.ViewDirection * (-1 * orbitRadius);
|
||||
Viewport.ViewPosition = TargetPoint + localPosition;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
/// <param name="orientation">The view rotation.</param>
|
||||
/// <param name="orbitCenter">The orbit center location.</param>
|
||||
/// <param name="orbitRadius">The orbit radius.</param>
|
||||
public void SetArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius)
|
||||
public virtual void SetArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius)
|
||||
{
|
||||
// Rotate
|
||||
Viewport.ViewOrientation = orientation;
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace FlaxEditor.Viewport
|
||||
public bool SnapToGround => Editor.Instance.Options.Options.Input.SnapToGround.Process(Root);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2 MouseDelta => _mouseDeltaLeft * 1000;
|
||||
public Vector2 MouseDelta => _mouseDelta * 1000;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root.GetKey(KeyboardKeys.Control);
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace FlaxEditor.Viewport
|
||||
private bool _isControllingMouse;
|
||||
private int _deltaFilteringStep;
|
||||
private Vector2 _startPos;
|
||||
private Vector2 _mouseDeltaRightLast;
|
||||
private Vector2 _mouseDeltaLast;
|
||||
private Vector2[] _deltaFilteringBuffer = new Vector2[FpsCameraFilteringFrames];
|
||||
|
||||
/// <summary>
|
||||
@@ -159,14 +159,9 @@ namespace FlaxEditor.Viewport
|
||||
protected Vector2 _viewMousePos;
|
||||
|
||||
/// <summary>
|
||||
/// The mouse delta (right button down).
|
||||
/// The mouse position delta.
|
||||
/// </summary>
|
||||
protected Vector2 _mouseDeltaRight;
|
||||
|
||||
/// <summary>
|
||||
/// The mouse delta (left button down).
|
||||
/// </summary>
|
||||
protected Vector2 _mouseDeltaLeft;
|
||||
protected Vector2 _mouseDelta;
|
||||
|
||||
// Camera
|
||||
|
||||
@@ -213,14 +208,9 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse movement delta for the right button (user press and move).
|
||||
/// Gets the mouse movement position delta (user press and move).
|
||||
/// </summary>
|
||||
public Vector2 MouseDeltaRight => _mouseDeltaRight;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse movement delta for the left button (user press and move).
|
||||
/// </summary>
|
||||
public Vector2 MouseDeltaLeft => _mouseDeltaLeft;
|
||||
public Vector2 MousePositionDelta => _mouseDelta;
|
||||
|
||||
/// <summary>
|
||||
/// Camera's pitch angle clamp range (in degrees).
|
||||
@@ -875,9 +865,14 @@ namespace FlaxEditor.Viewport
|
||||
win.StartTrackingMouse(false);
|
||||
win.Cursor = CursorType.Hidden;
|
||||
|
||||
// Center mouse position
|
||||
//_viewMousePos = Center;
|
||||
//win.MousePosition = PointToWindow(_viewMousePos);
|
||||
// Center mouse position if it's too close to the edge
|
||||
var size = Size;
|
||||
var center = size * 0.5f;
|
||||
if (Mathf.Abs(_viewMousePos.X - center.X) > center.X * 0.8f || Mathf.Abs(_viewMousePos.Y - center.Y) > center.Y * 0.8f)
|
||||
{
|
||||
_viewMousePos = center;
|
||||
win.MousePosition = PointToWindow(_viewMousePos);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1018,6 +1013,7 @@ namespace FlaxEditor.Viewport
|
||||
if (_isControllingMouse)
|
||||
{
|
||||
var rmbWheel = false;
|
||||
|
||||
// Gather input
|
||||
{
|
||||
bool isAltDown = _input.IsAltDown;
|
||||
@@ -1099,23 +1095,21 @@ namespace FlaxEditor.Viewport
|
||||
moveDelta *= 0.3f;
|
||||
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
|
||||
Vector2 offset = _viewMousePos - _startPos;
|
||||
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel)
|
||||
{
|
||||
offset = Vector2.Zero;
|
||||
}
|
||||
|
||||
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
||||
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
||||
_mouseDeltaRight = offset / size;
|
||||
_mouseDeltaRight.Y *= size.Y / size.X;
|
||||
_mouseDelta = offset / size;
|
||||
_mouseDelta.Y *= size.Y / size.X;
|
||||
|
||||
Vector2 mouseDelta = Vector2.Zero;
|
||||
if (_useMouseFiltering)
|
||||
{
|
||||
// Update delta filtering buffer
|
||||
_deltaFilteringBuffer[_deltaFilteringStep] = _mouseDeltaRight;
|
||||
_deltaFilteringBuffer[_deltaFilteringStep] = _mouseDelta;
|
||||
_deltaFilteringStep++;
|
||||
|
||||
// If the step is too far, zero
|
||||
@@ -1129,14 +1123,16 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta /= FpsCameraFilteringFrames;
|
||||
}
|
||||
else
|
||||
mouseDelta = _mouseDeltaRight;
|
||||
{
|
||||
mouseDelta = _mouseDelta;
|
||||
}
|
||||
|
||||
if (_useMouseAcceleration)
|
||||
{
|
||||
// Accelerate the delta
|
||||
var currentDelta = mouseDelta;
|
||||
mouseDelta += _mouseDeltaRightLast * _mouseAccelerationScale;
|
||||
_mouseDeltaRightLast = currentDelta;
|
||||
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
|
||||
_mouseDeltaLast = currentDelta;
|
||||
}
|
||||
|
||||
// Update
|
||||
@@ -1161,7 +1157,20 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
_mouseDeltaRight = _mouseDeltaRightLast = Vector2.Zero;
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
Vector2 offset = _viewMousePos - _startPos;
|
||||
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
||||
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
||||
_mouseDelta = offset / size;
|
||||
_startPos = _viewMousePos;
|
||||
}
|
||||
else
|
||||
{
|
||||
_mouseDelta = Vector2.Zero;
|
||||
}
|
||||
_mouseDeltaLast = Vector2.Zero;
|
||||
|
||||
if (ContainsFocus)
|
||||
{
|
||||
@@ -1198,19 +1207,6 @@ namespace FlaxEditor.Viewport
|
||||
UpdateView(dt, ref moveDelta, ref mouseDelta, out _);
|
||||
}
|
||||
}
|
||||
if (_input.IsMouseLeftDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
Vector2 offset = _viewMousePos - _startPos;
|
||||
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
||||
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
||||
_mouseDeltaLeft = offset / size;
|
||||
_startPos = _viewMousePos;
|
||||
}
|
||||
else
|
||||
{
|
||||
_mouseDeltaLeft = Vector2.Zero;
|
||||
}
|
||||
|
||||
_input.MouseWheelDelta = 0;
|
||||
}
|
||||
|
||||
@@ -797,6 +797,8 @@ namespace FlaxEditor.Viewport
|
||||
return true;
|
||||
if (assetItem.IsOfType<ModelBase>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<CollisionData>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<AudioClip>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<Prefab>())
|
||||
@@ -860,6 +862,13 @@ namespace FlaxEditor.Viewport
|
||||
return location;
|
||||
}
|
||||
|
||||
private void Spawn(Actor actor, ref Vector3 hitLocation)
|
||||
{
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Focus();
|
||||
}
|
||||
|
||||
private void Spawn(AssetItem item, SceneGraphNode hit, ref Vector2 location, ref Vector3 hitLocation)
|
||||
{
|
||||
if (item is AssetItem assetItem)
|
||||
@@ -872,8 +881,7 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
ParticleSystem = asset
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (assetItem.IsOfType<SceneAnimation>())
|
||||
@@ -884,8 +892,7 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
Animation = asset
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (assetItem.IsOfType<MaterialBase>())
|
||||
@@ -921,8 +928,7 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
SkinnedModel = model
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (assetItem.IsOfType<Model>())
|
||||
@@ -933,8 +939,18 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
Model = model
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (assetItem.IsOfType<CollisionData>())
|
||||
{
|
||||
var collisionData = FlaxEngine.Content.LoadAsync<CollisionData>(item.ID);
|
||||
var actor = new MeshCollider
|
||||
{
|
||||
Name = item.ShortName,
|
||||
CollisionData = collisionData
|
||||
};
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (assetItem.IsOfType<AudioClip>())
|
||||
@@ -945,8 +961,7 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
Clip = clip
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (assetItem.IsOfType<Prefab>())
|
||||
@@ -954,8 +969,7 @@ namespace FlaxEditor.Viewport
|
||||
var prefab = FlaxEngine.Content.LoadAsync<Prefab>(item.ID);
|
||||
var actor = PrefabManager.SpawnPrefab(prefab, null);
|
||||
actor.Name = item.ShortName;
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (assetItem.IsOfType<SceneAsset>())
|
||||
@@ -967,8 +981,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
var actor = (Actor)visualScriptItem.ScriptType.CreateInstance();
|
||||
actor.Name = item.ShortName;
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -983,8 +996,7 @@ namespace FlaxEditor.Viewport
|
||||
return;
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -342,7 +342,7 @@ namespace FlaxEditor.Viewport
|
||||
public bool SnapToGround => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2 MouseDelta => _mouseDeltaLeft * 1000;
|
||||
public Vector2 MouseDelta => _mouseDelta * 1000;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root.GetKey(KeyboardKeys.Control);
|
||||
@@ -576,7 +576,7 @@ namespace FlaxEditor.Viewport
|
||||
// Selected UI controls outline
|
||||
for (var i = 0; i < _window.Selection.Count; i++)
|
||||
{
|
||||
if (_window.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null)
|
||||
if (_window.Selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
|
||||
{
|
||||
var control = controlActor.Control;
|
||||
var bounds = Rectangle.FromPoints(control.PointToParent(this, Vector2.Zero), control.PointToParent(this, control.Size));
|
||||
@@ -682,10 +682,14 @@ namespace FlaxEditor.Viewport
|
||||
return true;
|
||||
if (assetItem.IsOfType<ModelBase>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<CollisionData>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<AudioClip>())
|
||||
return true;
|
||||
if (assetItem.IsOfType<Prefab>())
|
||||
return true;
|
||||
if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -746,8 +750,7 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
ParticleSystem = particleSystem
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (typeof(MaterialBase).IsAssignableFrom(binaryAssetItem.Type))
|
||||
@@ -773,8 +776,7 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
SkinnedModel = model
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (typeof(Model).IsAssignableFrom(binaryAssetItem.Type))
|
||||
@@ -785,8 +787,18 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
Model = model
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (binaryAssetItem.IsOfType<CollisionData>())
|
||||
{
|
||||
var collisionData = FlaxEngine.Content.LoadAsync<CollisionData>(item.ID);
|
||||
var actor = new MeshCollider
|
||||
{
|
||||
Name = item.ShortName,
|
||||
CollisionData = collisionData
|
||||
};
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (typeof(AudioClip).IsAssignableFrom(binaryAssetItem.Type))
|
||||
@@ -797,8 +809,7 @@ namespace FlaxEditor.Viewport
|
||||
Name = item.ShortName,
|
||||
Clip = clip
|
||||
};
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
if (typeof(Prefab).IsAssignableFrom(binaryAssetItem.Type))
|
||||
@@ -806,16 +817,24 @@ namespace FlaxEditor.Viewport
|
||||
var prefab = FlaxEngine.Content.LoadAsync<Prefab>(item.ID);
|
||||
var actor = PrefabManager.SpawnPrefab(prefab, null);
|
||||
actor.Name = item.ShortName;
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (item is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance)
|
||||
{
|
||||
var actor = (Actor)visualScriptItem.ScriptType.CreateInstance();
|
||||
actor.Name = item.ShortName;
|
||||
Spawn(actor, ref hitLocation);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void Spawn(Actor actor)
|
||||
private void Spawn(Actor actor, ref Vector3 hitLocation)
|
||||
{
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
_window.Spawn(actor);
|
||||
Focus();
|
||||
}
|
||||
|
||||
private void Spawn(ScriptType item, SceneGraphNode hit, ref Vector3 hitLocation)
|
||||
@@ -827,8 +846,7 @@ namespace FlaxEditor.Viewport
|
||||
return;
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
Spawn(actor);
|
||||
Spawn(actor, ref hitLocation);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -249,6 +249,19 @@ namespace FlaxEditor.Viewport.Previews
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.F:
|
||||
// Pay respect..
|
||||
ViewportCamera.SetArcBallView(_previewModel.Box);
|
||||
break;
|
||||
}
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -85,6 +85,19 @@ namespace FlaxEditor.Viewport.Previews
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.F:
|
||||
// Pay respect..
|
||||
ViewportCamera.SetArcBallView(_previewModel.Box);
|
||||
break;
|
||||
}
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -121,6 +121,18 @@ namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_instance != null)
|
||||
{
|
||||
// Link UI canvases to the preview (eg. after canvas added to the prefab)
|
||||
LinkCanvas(_instance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
private readonly ParticleSystemWindow _window;
|
||||
|
||||
[EditorDisplay("Particle System"), EditorOrder(100), Limit(1), Tooltip("The timeline animation duration in frames.")]
|
||||
[EditorDisplay("Particle System"), EditorOrder(-100), Limit(1), Tooltip("The timeline animation duration in frames.")]
|
||||
public int TimelineDurationFrames
|
||||
{
|
||||
get => _window.Timeline.DurationFrames;
|
||||
@@ -124,7 +124,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// The proxy object for editing particle system track properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(EmitterTrackProxyEditor))]
|
||||
private class EmitterTrackProxy
|
||||
private class EmitterTrackProxy : GeneralProxy
|
||||
{
|
||||
private readonly ParticleSystemWindow _window;
|
||||
private readonly ParticleEffect _effect;
|
||||
@@ -171,6 +171,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
|
||||
public EmitterTrackProxy(ParticleSystemWindow window, ParticleEffect effect, ParticleEmitterTrack track, int emitterIndex)
|
||||
: base(window)
|
||||
{
|
||||
_window = window;
|
||||
_effect = effect;
|
||||
@@ -238,7 +239,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <summary>
|
||||
/// The proxy object for editing folder track properties.
|
||||
/// </summary>
|
||||
private class FolderTrackProxy
|
||||
private class FolderTrackProxy : GeneralProxy
|
||||
{
|
||||
private readonly FolderTrack _track;
|
||||
|
||||
@@ -265,7 +266,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
set => _track.IconColor = value;
|
||||
}
|
||||
|
||||
public FolderTrackProxy(FolderTrack track)
|
||||
public FolderTrackProxy(ParticleSystemWindow window, FolderTrack track)
|
||||
: base(window)
|
||||
{
|
||||
_track = track;
|
||||
}
|
||||
@@ -275,8 +277,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private readonly SplitPanel _split2;
|
||||
private ParticleSystemTimeline _timeline;
|
||||
private readonly ParticleSystemPreview _preview;
|
||||
private readonly CustomEditorPresenter _propertiesEditor1;
|
||||
private readonly CustomEditorPresenter _propertiesEditor2;
|
||||
private readonly CustomEditorPresenter _propertiesEditor;
|
||||
private ToolStripButton _saveButton;
|
||||
private ToolStripButton _undoButton;
|
||||
private ToolStripButton _redoButton;
|
||||
@@ -347,18 +348,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
_timeline.Modified += OnTimelineModified;
|
||||
_timeline.SelectionChanged += OnTimelineSelectionChanged;
|
||||
|
||||
// Properties editor (general)
|
||||
var propertiesEditor1 = new CustomEditorPresenter(null, string.Empty);
|
||||
propertiesEditor1.Panel.Parent = _split2.Panel2;
|
||||
propertiesEditor1.Modified += OnParticleSystemPropertyEdited;
|
||||
_propertiesEditor1 = propertiesEditor1;
|
||||
propertiesEditor1.Select(new GeneralProxy(this));
|
||||
|
||||
// Properties editor (selection)
|
||||
var propertiesEditor2 = new CustomEditorPresenter(null, string.Empty);
|
||||
propertiesEditor2.Panel.Parent = _split2.Panel2;
|
||||
propertiesEditor2.Modified += OnParticleSystemPropertyEdited;
|
||||
_propertiesEditor2 = propertiesEditor2;
|
||||
// Properties editor
|
||||
var propertiesEditor = new CustomEditorPresenter(_undo, string.Empty);
|
||||
propertiesEditor.Panel.Parent = _split2.Panel2;
|
||||
propertiesEditor.Modified += OnParticleSystemPropertyEdited;
|
||||
_propertiesEditor = propertiesEditor;
|
||||
propertiesEditor.Select(new GeneralProxy(this));
|
||||
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save32, Save).LinkTooltip("Save");
|
||||
@@ -380,8 +375,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
if (!_isEditingInstancedParameterValue)
|
||||
{
|
||||
_propertiesEditor1.BuildLayoutOnUpdate();
|
||||
_propertiesEditor2.BuildLayoutOnUpdate();
|
||||
_propertiesEditor.BuildLayoutOnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +393,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
if (_timeline.SelectedTracks.Count == 0)
|
||||
{
|
||||
_propertiesEditor2.Deselect();
|
||||
_propertiesEditor.Select(new GeneralProxy(this));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -414,14 +408,14 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
else if (track is FolderTrack folderTrack)
|
||||
{
|
||||
tracks[i] = new FolderTrackProxy(folderTrack);
|
||||
tracks[i] = new FolderTrackProxy(this, folderTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("Invalid track type.");
|
||||
}
|
||||
}
|
||||
_propertiesEditor2.Select(tracks);
|
||||
_propertiesEditor.Select(tracks);
|
||||
}
|
||||
|
||||
private void OnParticleSystemPropertyEdited()
|
||||
@@ -443,8 +437,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
if (_timeline.IsModified)
|
||||
{
|
||||
_propertiesEditor1.BuildLayoutOnUpdate();
|
||||
_propertiesEditor2.BuildLayoutOnUpdate();
|
||||
_propertiesEditor.BuildLayoutOnUpdate();
|
||||
_timeline.Save(_asset);
|
||||
}
|
||||
|
||||
@@ -484,7 +477,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <inheritdoc />
|
||||
protected override void UnlinkItem()
|
||||
{
|
||||
_propertiesEditor2.Deselect();
|
||||
_propertiesEditor.Deselect();
|
||||
_preview.System = null;
|
||||
_isWaitingForTimelineLoad = false;
|
||||
|
||||
@@ -507,7 +500,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
if (_parametersVersion != _preview.PreviewActor.ParametersVersion)
|
||||
{
|
||||
_parametersVersion = _preview.PreviewActor.ParametersVersion;
|
||||
_propertiesEditor2.BuildLayoutOnUpdate();
|
||||
_propertiesEditor.BuildLayoutOnUpdate();
|
||||
}
|
||||
|
||||
base.Update(deltaTime);
|
||||
@@ -534,8 +527,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Setup
|
||||
_undo.Clear();
|
||||
_timeline.Enabled = true;
|
||||
_propertiesEditor1.BuildLayout();
|
||||
_propertiesEditor2.Deselect();
|
||||
_propertiesEditor.Select(new GeneralProxy(this));
|
||||
ClearEditedFlag();
|
||||
}
|
||||
|
||||
@@ -580,8 +572,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
if (_undo != null)
|
||||
_undo.Enabled = false;
|
||||
_propertiesEditor1?.Deselect();
|
||||
_propertiesEditor2?.Deselect();
|
||||
_propertiesEditor?.Deselect();
|
||||
_undo?.Clear();
|
||||
_undo = null;
|
||||
|
||||
|
||||
@@ -296,7 +296,7 @@ namespace FlaxEditor.Windows
|
||||
// Selected UI controls outline
|
||||
for (var i = 0; i < Editor.Instance.SceneEditing.Selection.Count; i++)
|
||||
{
|
||||
if (Editor.Instance.SceneEditing.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null)
|
||||
if (Editor.Instance.SceneEditing.Selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
|
||||
{
|
||||
var control = controlActor.Control;
|
||||
var bounds = Rectangle.FromPoints(control.PointToParent(_viewport, Vector2.Zero), control.PointToParent(_viewport, control.Size));
|
||||
|
||||
@@ -451,7 +451,69 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu
|
||||
// Get parameter
|
||||
int32 paramIndex;
|
||||
const auto param = _graph.GetParameter((Guid)node->Values[0], paramIndex);
|
||||
value = param ? _data->Parameters[paramIndex].Value : Value::Null;
|
||||
if (param)
|
||||
{
|
||||
value = _data->Parameters[paramIndex].Value;
|
||||
switch (param->Type.Type)
|
||||
{
|
||||
case VariantType::Vector2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
value = value.AsVector2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Vector3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
value = value.AsVector3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Vector4:
|
||||
case VariantType::Color:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
value = value.AsVector4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Matrix:
|
||||
{
|
||||
auto& matrix = value.Type.Type == VariantType::Matrix && value.AsBlob.Data ? *(Matrix*)value.AsBlob.Data : Matrix::Identity;
|
||||
switch (box->ID)
|
||||
{
|
||||
case 0:
|
||||
value = matrix.GetRow1();
|
||||
break;
|
||||
case 1:
|
||||
value = matrix.GetRow2();
|
||||
break;
|
||||
case 2:
|
||||
value = matrix.GetRow3();
|
||||
break;
|
||||
case 3:
|
||||
value = matrix.GetRow4();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: add warning that no parameter selected
|
||||
value = Value::Zero;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1132,6 +1132,6 @@ void SceneAnimationPlayer::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ void AudioListener::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
|
||||
if (IsActiveInHierarchy())
|
||||
|
||||
@@ -461,7 +461,7 @@ void AudioSource::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
|
||||
if (IsActiveInHierarchy() && SourceIDs.HasItems())
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "../Types/String.h"
|
||||
|
||||
const BoundingBox BoundingBox::Empty(Vector3(MAX_float), Vector3(MIN_float));
|
||||
const BoundingBox BoundingBox::Zero(Vector3(0.0f), Vector3(0.0f));
|
||||
const BoundingBox BoundingBox::Zero(Vector3(0.0f));
|
||||
|
||||
String BoundingBox::ToString() const
|
||||
{
|
||||
|
||||
@@ -45,6 +45,16 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoundingBox"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="point">The location of the empty bounding box.</param>
|
||||
BoundingBox(const Vector3& point)
|
||||
: Minimum(point)
|
||||
, Maximum(point)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoundingBox"/> struct.
|
||||
/// </summary>
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace FlaxEngine
|
||||
public static class CollisionsHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines the closest point between a point and a line.
|
||||
/// Determines the closest point between a point and a line segment.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to test.</param>
|
||||
/// <param name="p0">The line first point.</param>
|
||||
|
||||
@@ -197,6 +197,16 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public float ValuesSum => X + Y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vector with values being absolute values of that vector.
|
||||
/// </summary>
|
||||
public Vector2 Absolute => new Vector2(Mathf.Abs(X), Mathf.Abs(Y));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vector with values being opposite to values of that vector.
|
||||
/// </summary>
|
||||
public Vector2 Negative => new Vector2(-X, -Y);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the component at the specified index.
|
||||
/// </summary>
|
||||
|
||||
@@ -227,6 +227,16 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public float ValuesSum => X + Y + Z + W;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vector with values being absolute values of that vector.
|
||||
/// </summary>
|
||||
public Vector4 Absolute => new Vector4(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z), Mathf.Abs(W));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vector with values being opposite to values of that vector.
|
||||
/// </summary>
|
||||
public Vector4 Negative => new Vector4(-X, -Y, -Z, -W);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the component at the specified index.
|
||||
/// </summary>
|
||||
|
||||
@@ -413,24 +413,6 @@ bool Engine::HasGameViewportFocus()
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector2 Engine::ScreenToGameViewport(const Vector2& screenPos)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
return Editor::Managed->ScreenToGameViewport(screenPos);
|
||||
#else
|
||||
return MainWindow ? MainWindow->ScreenToClient(screenPos) : Vector2::Minimum;
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector2 Engine::GameViewportToScreen(const Vector2& viewportPos)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
return Editor::Managed->GameViewportToScreen(viewportPos);
|
||||
#else
|
||||
return MainWindow ? MainWindow->ClientToScreen(viewportPos) : Vector2::Minimum;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Engine::OnPause()
|
||||
{
|
||||
LOG(Info, "App paused");
|
||||
|
||||
@@ -153,20 +153,6 @@ public:
|
||||
/// <returns>True if game viewport is focused, otherwise false.</returns>
|
||||
static bool HasGameViewportFocus();
|
||||
|
||||
/// <summary>
|
||||
/// Converts the screen-space position to the game viewport position.
|
||||
/// </summary>
|
||||
/// <param name="screenPos">The screen-space position.</param>
|
||||
/// <returns>The game viewport position.</returns>
|
||||
static Vector2 ScreenToGameViewport(const Vector2& screenPos);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the game viewport position to the screen-space position.
|
||||
/// </summary>
|
||||
/// <param name="viewportPos">The game viewport position.</param>
|
||||
/// <returns>The screen-space position.</returns>
|
||||
static Vector2 GameViewportToScreen(const Vector2& viewportPos);
|
||||
|
||||
private:
|
||||
|
||||
static void OnPause();
|
||||
|
||||
@@ -66,6 +66,26 @@ void Screen::SetSize(const Vector2& value)
|
||||
Size = value;
|
||||
}
|
||||
|
||||
Vector2 Screen::ScreenToGameViewport(const Vector2& screenPos)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
return Editor::Managed->ScreenToGameViewport(screenPos);
|
||||
#else
|
||||
auto win = Engine::MainWindow;
|
||||
return win ? win->ScreenToClient(screenPos) : Vector2::Minimum;
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector2 Screen::GameViewportToScreen(const Vector2& viewportPos)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
return Editor::Managed->GameViewportToScreen(viewportPos);
|
||||
#else
|
||||
auto win = Engine::MainWindow;
|
||||
return win ? win->ClientToScreen(viewportPos) : Vector2::Minimum;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Screen::GetCursorVisible()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -33,6 +33,20 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Screen);
|
||||
/// <returns>The value</returns>
|
||||
API_PROPERTY() static Vector2 GetSize();
|
||||
|
||||
/// <summary>
|
||||
/// Converts the screen-space position to the game viewport position.
|
||||
/// </summary>
|
||||
/// <param name="screenPos">The screen-space position.</param>
|
||||
/// <returns>The game viewport position.</returns>
|
||||
API_FUNCTION() static Vector2 ScreenToGameViewport(const Vector2& screenPos);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the game viewport position to the screen-space position.
|
||||
/// </summary>
|
||||
/// <param name="viewportPos">The game viewport position.</param>
|
||||
/// <returns>The screen-space position.</returns>
|
||||
API_FUNCTION() static Vector2 GameViewportToScreen(const Vector2& viewportPos);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the window size.
|
||||
/// </summary>
|
||||
|
||||
@@ -339,14 +339,14 @@ bool Input::GetKeyUp(const KeyboardKeys key)
|
||||
|
||||
Vector2 Input::GetMousePosition()
|
||||
{
|
||||
return Mouse ? Engine::ScreenToGameViewport(Mouse->GetPosition()) : Vector2::Minimum;
|
||||
return Mouse ? Screen::ScreenToGameViewport(Mouse->GetPosition()) : Vector2::Minimum;
|
||||
}
|
||||
|
||||
void Input::SetMousePosition(const Vector2& position)
|
||||
{
|
||||
if (Mouse && Engine::HasGameViewportFocus())
|
||||
{
|
||||
const auto pos = Engine::GameViewportToScreen(position);
|
||||
const auto pos = Screen::GameViewportToScreen(position);
|
||||
if (pos > Vector2::Minimum)
|
||||
Mouse->SetMousePosition(pos);
|
||||
}
|
||||
|
||||
@@ -203,6 +203,13 @@ void Actor::SetParent(Actor* value, bool worldPositionsStays, bool canBreakPrefa
|
||||
LOG(Error, "Editing scene hierarchy is only allowed on a main thread.");
|
||||
return;
|
||||
}
|
||||
#if USE_EDITOR || !BUILD_RELEASE
|
||||
if (Is<Scene>())
|
||||
{
|
||||
LOG(Error, "Cannot change parent of the Scene. Use Level to manage scenes.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Peek the previous state
|
||||
const Transform prevTransform = _transform;
|
||||
@@ -945,28 +952,34 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
DESERIALIZE_MEMBER(Name, _name);
|
||||
DESERIALIZE_MEMBER(Transform, _localTransform);
|
||||
|
||||
Guid parentId = Guid::Empty;
|
||||
DESERIALIZE_MEMBER(ParentID, parentId);
|
||||
const auto parent = Scripting::FindObject<Actor>(parentId);
|
||||
if (_parent != parent)
|
||||
{
|
||||
if (IsDuringPlay())
|
||||
const auto member = SERIALIZE_FIND_MEMBER(stream, "ParentID");
|
||||
if (member != stream.MemberEnd())
|
||||
{
|
||||
SetParent(parent, false, false);
|
||||
Guid parentId;
|
||||
Serialization::Deserialize(member->value, parentId, modifier);
|
||||
const auto parent = Scripting::FindObject<Actor>(parentId);
|
||||
if (_parent != parent)
|
||||
{
|
||||
if (IsDuringPlay())
|
||||
{
|
||||
SetParent(parent, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_parent)
|
||||
_parent->Children.RemoveKeepOrder(this);
|
||||
_parent = parent;
|
||||
if (_parent)
|
||||
_parent->Children.Add(this);
|
||||
OnParentChanged();
|
||||
}
|
||||
}
|
||||
else if (!parent && parentId.IsValid())
|
||||
{
|
||||
LOG(Warning, "Missing parent actor {0} for \'{1}\'", parentId, ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_parent)
|
||||
_parent->Children.RemoveKeepOrder(this);
|
||||
_parent = parent;
|
||||
if (_parent)
|
||||
_parent->Children.Add(this);
|
||||
OnParentChanged();
|
||||
}
|
||||
}
|
||||
else if (!parent && parentId.IsValid())
|
||||
{
|
||||
LOG(Warning, "Missing parent actor {0} for \'{1}\'", parentId, ToString());
|
||||
}
|
||||
|
||||
// StaticFlags update - added StaticFlags::Navigation
|
||||
@@ -1133,20 +1146,28 @@ BoundingBox Actor::GetBoxWithChildren() const
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
BoundingBox Actor::GetEditorBox() const
|
||||
{
|
||||
return GetBox();
|
||||
}
|
||||
|
||||
BoundingBox Actor::GetEditorBoxChildren() const
|
||||
{
|
||||
BoundingBox result = GetEditorBox();
|
||||
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
BoundingBox::Merge(result, Children[i]->GetEditorBoxChildren(), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool Actor::HasContentLoaded() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Actor::UnregisterObjectHierarchy()
|
||||
{
|
||||
if (IsRegistered())
|
||||
@@ -1164,6 +1185,10 @@ void Actor::UnregisterObjectHierarchy()
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::Draw(RenderContext& renderContext)
|
||||
{
|
||||
}
|
||||
|
||||
void Actor::DrawGeneric(RenderContext& renderContext)
|
||||
{
|
||||
// Generic drawing uses only GBuffer Fill Pass and simple frustum culling (see SceneRendering for more optimized drawing)
|
||||
|
||||
@@ -629,10 +629,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets actor bounding box (single actor, no children included) for editor tools.
|
||||
/// </summary>
|
||||
API_PROPERTY() virtual BoundingBox GetEditorBox() const
|
||||
{
|
||||
return GetBox();
|
||||
}
|
||||
API_PROPERTY() virtual BoundingBox GetEditorBox() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets actor bounding box of the actor including all child actors for editor tools.
|
||||
@@ -644,10 +641,7 @@ public:
|
||||
/// <summary>
|
||||
/// Returns true if actor has loaded content.
|
||||
/// </summary>
|
||||
API_PROPERTY() virtual bool HasContentLoaded() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
API_PROPERTY() virtual bool HasContentLoaded() const;
|
||||
|
||||
/// <summary>
|
||||
/// Calls UnregisterObject for all objects in the actor hierarchy.
|
||||
@@ -660,9 +654,7 @@ public:
|
||||
/// Draws this actor. Called by Scene Rendering service. This call is more optimized than generic Draw (eg. models are rendered during all passed but other actors are invoked only during GBufferFill pass).
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
virtual void Draw(RenderContext& renderContext)
|
||||
{
|
||||
}
|
||||
virtual void Draw(RenderContext& renderContext);
|
||||
|
||||
/// <summary>
|
||||
/// Draws this actor. Called during custom actor rendering or any other generic rendering from code.
|
||||
|
||||
@@ -383,7 +383,7 @@ void AnimatedModel::UpdateLocalBounds()
|
||||
}
|
||||
else
|
||||
{
|
||||
box = BoundingBox(Vector3::Zero, Vector3::Zero);
|
||||
box = BoundingBox(Vector3::Zero);
|
||||
}
|
||||
|
||||
// Scale bounds
|
||||
|
||||
@@ -96,7 +96,7 @@ void BoneSocket::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,12 +101,12 @@ void Camera::SetOrthographicScale(float value)
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::ProjectPoint(const Vector3& worldSpaceLocation, Vector2& screenSpaceLocation) const
|
||||
void Camera::ProjectPoint(const Vector3& worldSpaceLocation, Vector2& gameWindowSpaceLocation) const
|
||||
{
|
||||
ProjectPoint(worldSpaceLocation, screenSpaceLocation, GetViewport());
|
||||
ProjectPoint(worldSpaceLocation, gameWindowSpaceLocation, GetViewport());
|
||||
}
|
||||
|
||||
void Camera::ProjectPoint(const Vector3& worldSpaceLocation, Vector2& screenSpaceLocation, const Viewport& viewport) const
|
||||
void Camera::ProjectPoint(const Vector3& worldSpaceLocation, Vector2& cameraViewportSpaceLocation, const Viewport& viewport) const
|
||||
{
|
||||
Matrix v, p, vp;
|
||||
GetMatrices(v, p, viewport);
|
||||
@@ -114,7 +114,7 @@ void Camera::ProjectPoint(const Vector3& worldSpaceLocation, Vector2& screenSpac
|
||||
Vector3 clipSpaceLocation;
|
||||
Vector3::Transform(worldSpaceLocation, vp, clipSpaceLocation);
|
||||
viewport.Project(worldSpaceLocation, vp, clipSpaceLocation);
|
||||
screenSpaceLocation = Vector2(clipSpaceLocation);
|
||||
cameraViewportSpaceLocation = Vector2(clipSpaceLocation);
|
||||
}
|
||||
|
||||
Ray Camera::ConvertMouseToRay(const Vector2& mousePosition) const
|
||||
|
||||
@@ -174,19 +174,19 @@ public:
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Projects the point from 3D world-space to the camera screen-space (in screen pixels for default viewport calculated from <see cref="Viewport"/>).
|
||||
/// Projects the point from 3D world-space to game window coordinates (in screen pixels for default viewport calculated from <see cref="Viewport"/>).
|
||||
/// </summary>
|
||||
/// <param name="worldSpaceLocation">The input world-space location (XYZ in world).</param>
|
||||
/// <param name="screenSpaceLocation">The output screen-space location (XY in screen pixels).</param>
|
||||
API_FUNCTION() void ProjectPoint(const Vector3& worldSpaceLocation, API_PARAM(Out) Vector2& screenSpaceLocation) const;
|
||||
/// <param name="gameWindowSpaceLocation">The output game window coordinates (XY in screen pixels).</param>
|
||||
API_FUNCTION() void ProjectPoint(const Vector3& worldSpaceLocation, API_PARAM(Out) Vector2& gameWindowSpaceLocation) const;
|
||||
|
||||
/// <summary>
|
||||
/// Projects the point from 3D world-space to the camera screen-space (in screen pixels for given viewport).
|
||||
/// Projects the point from 3D world-space to the camera viewport-space (in screen pixels for given viewport).
|
||||
/// </summary>
|
||||
/// <param name="worldSpaceLocation">The input world-space location (XYZ in world).</param>
|
||||
/// <param name="screenSpaceLocation">The output screen-space location (XY in screen pixels).</param>
|
||||
/// <param name="cameraViewportSpaceLocation">The output camera viewport-space location (XY in screen pixels).</param>
|
||||
/// <param name="viewport">The viewport.</param>
|
||||
API_FUNCTION() void ProjectPoint(const Vector3& worldSpaceLocation, API_PARAM(Out) Vector2& screenSpaceLocation, API_PARAM(Ref) const Viewport& viewport) const;
|
||||
API_FUNCTION() void ProjectPoint(const Vector3& worldSpaceLocation, API_PARAM(Out) Vector2& cameraViewportSpaceLocation, API_PARAM(Ref) const Viewport& viewport) const;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the mouse position to 3D ray.
|
||||
|
||||
@@ -92,6 +92,6 @@ void DirectionalLight::OnTransformChanged()
|
||||
// Base
|
||||
LightWithShadow::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,6 @@ void EmptyActor::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
@@ -228,6 +228,6 @@ void ExponentialHeightFog::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
@@ -259,6 +259,6 @@ void Sky::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
@@ -146,6 +146,6 @@ void Skybox::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#include "Spline.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Animations/CurveSerialization.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include <ThirdParty/mono-2.0/mono/metadata/object.h>
|
||||
|
||||
Spline::Spline(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
, _localBounds(Vector3::Zero, Vector3::Zero)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -411,17 +413,27 @@ void Spline::SetTangentsSmooth()
|
||||
|
||||
void Spline::UpdateSpline()
|
||||
{
|
||||
auto& keyframes = Curve.GetKeyframes();
|
||||
const int32 count = keyframes.Count();
|
||||
|
||||
// Always keep last point in the loop
|
||||
const int32 count = Curve.GetKeyframes().Count();
|
||||
if (_loop && count > 1)
|
||||
{
|
||||
auto& first = Curve[0];
|
||||
auto& last = Curve[count - 1];
|
||||
auto& first = keyframes[0];
|
||||
auto& last = keyframes[count - 1];
|
||||
last.Value = first.Value;
|
||||
last.TangentIn = first.TangentIn;
|
||||
last.TangentOut = first.TangentOut;
|
||||
}
|
||||
|
||||
// Update bounds
|
||||
_localBounds = BoundingBox(count != 0 ? keyframes[0].Value.Translation : Vector3::Zero);
|
||||
for (int32 i = 1; i < count; i++)
|
||||
_localBounds.Merge(keyframes[i].Value.Translation);
|
||||
Matrix world;
|
||||
_transform.GetWorld(world);
|
||||
BoundingBox::Transform(_localBounds, world, _box);
|
||||
|
||||
SplineUpdated();
|
||||
}
|
||||
|
||||
@@ -485,6 +497,34 @@ void Spline::OnDebugDrawSelected()
|
||||
|
||||
#endif
|
||||
|
||||
void Spline::OnTransformChanged()
|
||||
{
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
Matrix world;
|
||||
_transform.GetWorld(world);
|
||||
BoundingBox::Transform(_localBounds, world, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
|
||||
void Spline::PostLoad()
|
||||
{
|
||||
// Base
|
||||
Actor::PostLoad();
|
||||
|
||||
auto& keyframes = Curve.GetKeyframes();
|
||||
const int32 count = keyframes.Count();
|
||||
|
||||
// Update bounds
|
||||
_localBounds = BoundingBox(count != 0 ? keyframes[0].Value.Translation : Vector3::Zero);
|
||||
for (int32 i = 1; i < count; i++)
|
||||
_localBounds.Merge(keyframes[i].Value.Translation);
|
||||
Matrix world;
|
||||
_transform.GetWorld(world);
|
||||
BoundingBox::Transform(_localBounds, world, _box);
|
||||
}
|
||||
|
||||
void Spline::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
// Base
|
||||
|
||||
@@ -15,6 +15,7 @@ DECLARE_SCENE_OBJECT(Spline);
|
||||
private:
|
||||
|
||||
bool _loop = false;
|
||||
BoundingBox _localBounds;
|
||||
|
||||
public:
|
||||
|
||||
@@ -377,6 +378,8 @@ public:
|
||||
void OnDebugDraw() override;
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
void OnTransformChanged() override;
|
||||
void PostLoad() override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
};
|
||||
|
||||
@@ -110,7 +110,7 @@ void SplineModel::OnSplineUpdated()
|
||||
// Skip updates when actor is disabled or something is missing
|
||||
if (!_spline || !Model || !Model->IsLoaded() || !IsActiveInHierarchy() || _spline->GetSplinePointsCount() < 2)
|
||||
{
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ void StaticModel::UpdateBounds()
|
||||
}
|
||||
else
|
||||
{
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
}
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
|
||||
@@ -1037,9 +1037,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, bool autoI
|
||||
|
||||
// Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it)
|
||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(*sceneObjects.Value, actorToRemovedObjectsData, modifier.Value);
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
|
||||
// Delete objects without parent
|
||||
for (int32 i = 1; i < objectsCount; i++)
|
||||
|
||||
@@ -358,27 +358,47 @@ bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& p
|
||||
{
|
||||
obj->Deserialize(instance.Data[dataIndex], modifier.Value);
|
||||
|
||||
// Send events because some properties may be modified during prefab changes apply
|
||||
// TODO: maybe send only valid events (need to track changes for before-after state)
|
||||
Actor* actor = dynamic_cast<Actor*>(obj);
|
||||
if (actor && actor->IsDuringPlay())
|
||||
// Preserve order in parent (values from prefab are used)
|
||||
if (i != 0)
|
||||
{
|
||||
Level::callActorEvent(Level::ActorEventType::OnActorNameChanged, actor, nullptr);
|
||||
Level::callActorEvent(Level::ActorEventType::OnActorActiveChanged, actor, nullptr);
|
||||
Level::callActorEvent(Level::ActorEventType::OnActorOrderInParentChanged, actor, nullptr);
|
||||
auto prefab = Content::Load<Prefab>(prefabId);
|
||||
const auto defaultInstance = prefab ? prefab->GetDefaultInstance(obj->GetPrefabObjectID()) : nullptr;
|
||||
if (defaultInstance)
|
||||
{
|
||||
obj->SetOrderInParent(defaultInstance->GetOrderInParent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
|
||||
// Setup objects after deserialization
|
||||
// Setup new objects after deserialization
|
||||
for (int32 i = existingObjectsCount; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects.Value->At(i);
|
||||
obj->PostLoad();
|
||||
}
|
||||
|
||||
// Synchronize existing objects logic with deserialized state (fire events)
|
||||
for (int32 i = 0; i < existingObjectsCount; i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
Actor* actor = dynamic_cast<Actor*>(obj);
|
||||
if (actor)
|
||||
{
|
||||
const bool shouldBeActiveInHierarchy = actor->GetIsActive() && (!actor->GetParent() || actor->GetParent()->IsActiveInHierarchy());
|
||||
if (shouldBeActiveInHierarchy != actor->IsActiveInHierarchy())
|
||||
{
|
||||
actor->_isActiveInHierarchy = shouldBeActiveInHierarchy;
|
||||
actor->OnActiveInTreeChanged();
|
||||
Level::callActorEvent(Level::ActorEventType::OnActorActiveChanged, actor, nullptr);
|
||||
}
|
||||
Level::callActorEvent(Level::ActorEventType::OnActorNameChanged, actor, nullptr);
|
||||
Level::callActorEvent(Level::ActorEventType::OnActorOrderInParentChanged, actor, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore order in parent
|
||||
instance.TargetActor->SetOrderInParent(instance.OrderInParent);
|
||||
|
||||
@@ -784,6 +804,20 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
obj->Deserialize(diffDataDocument[dataIndex], modifier.Value);
|
||||
|
||||
sceneObjects->Add(obj);
|
||||
|
||||
// Synchronize order of the scene objects with the serialized data (eg. user reordered actors in prefab editor and applied changes)
|
||||
if (i != 0)
|
||||
{
|
||||
for (int32 j = 0; j < targetObjects->Count(); j++)
|
||||
{
|
||||
SceneObject* targetObject = targetObjects->At(j);
|
||||
if (targetObject->GetPrefabObjectID() == obj->GetID())
|
||||
{
|
||||
obj->SetOrderInParent(targetObject->GetOrderInParent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -800,7 +834,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
sceneObjects->RemoveAtKeepOrder(i);
|
||||
}
|
||||
|
||||
// Deserialize new prefab objects (add new objects)
|
||||
// Deserialize new prefab objects
|
||||
int32 newPrefabInstanceIdToDataIndexCounter = 0;
|
||||
int32 newPrefabInstanceIdToDataIndexStart = sceneObjects->Count();
|
||||
sceneObjects->Resize(sceneObjects->Count() + newPrefabInstanceIdToDataIndex.Count());
|
||||
@@ -822,9 +856,27 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
{
|
||||
const int32 dataIndex = i->Value;
|
||||
SceneObject* obj = sceneObjects->At(newPrefabInstanceIdToDataIndexStart + newPrefabInstanceIdToDataIndexCounter++);
|
||||
if (obj)
|
||||
if (!obj)
|
||||
continue;
|
||||
SceneObjectsFactory::Deserialize(obj, diffDataDocument[dataIndex], modifier.Value);
|
||||
}
|
||||
for (int32 j = 0; j < targetObjects->Count(); j++)
|
||||
{
|
||||
auto obj = targetObjects->At(j);
|
||||
Guid prefabObjectId;
|
||||
if (newPrefabInstanceIdToPrefabObjectId.TryGet(obj->GetSceneObjectId(), prefabObjectId))
|
||||
{
|
||||
SceneObjectsFactory::Deserialize(obj, diffDataDocument[dataIndex], modifier.Value);
|
||||
newPrefabInstanceIdToDataIndexCounter = 0;
|
||||
for (auto i = newPrefabInstanceIdToDataIndex.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
SceneObject* e = sceneObjects->At(newPrefabInstanceIdToDataIndexStart + newPrefabInstanceIdToDataIndexCounter++);
|
||||
if (e->GetID() == prefabObjectId)
|
||||
{
|
||||
// Synchronize order of new objects with the order in target instance
|
||||
e->SetOrderInParent(obj->GetOrderInParent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
@@ -861,7 +913,10 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
auto obj = sceneObjects.Value->At(i);
|
||||
obj->PostLoad();
|
||||
if (obj)
|
||||
{
|
||||
obj->PostLoad();
|
||||
}
|
||||
}
|
||||
|
||||
// Update transformations
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "Engine/Core/Cache.h"
|
||||
#include "Engine/Debug/Exceptions/ArgumentException.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
|
||||
#if USE_EDITOR
|
||||
@@ -216,21 +217,57 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid
|
||||
|
||||
// TODO: consider caching actorToRemovedObjectsData per prefab
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(*sceneObjects.Value, actorToRemovedObjectsData, modifier.Value);
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
}
|
||||
|
||||
// Delete objects without parent
|
||||
// Delete objects without parent or with invalid linkage to the prefab
|
||||
for (int32 i = 1; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (obj && obj->GetParent() == nullptr)
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
// Check for missing parent (eg. parent object has been deleted)
|
||||
if (obj->GetParent() == nullptr)
|
||||
{
|
||||
sceneObjects->At(i) = nullptr;
|
||||
LOG(Warning, "Scene object {0} {1} has missing parent object after load. Removing it.", obj->GetID(), obj->ToString());
|
||||
obj->DeleteObject();
|
||||
continue;
|
||||
}
|
||||
|
||||
#if USE_EDITOR && !BUILD_RELEASE
|
||||
// Check for not being added to the parent (eg. invalid setup events fault on registration)
|
||||
auto actor = dynamic_cast<Actor*>(obj);
|
||||
auto script = dynamic_cast<Script*>(obj);
|
||||
if (obj->GetParent() == obj || (actor && !actor->GetParent()->Children.Contains(actor)) || (script && !script->GetParent()->Scripts.Contains(script)))
|
||||
{
|
||||
sceneObjects->At(i) = nullptr;
|
||||
LOG(Warning, "Scene object {0} {1} has invalid parent object linkage after load. Removing it.", obj->GetID(), obj->ToString());
|
||||
obj->DeleteObject();
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR && BUILD_DEBUG
|
||||
// Check for being added to parent not from spawned prefab (eg. invalid parentId linkage fault)
|
||||
bool hasParentInInstance = false;
|
||||
for (int32 j = 0; j < sceneObjects->Count(); j++)
|
||||
{
|
||||
if (sceneObjects->At(j) == obj->GetParent())
|
||||
{
|
||||
hasParentInInstance = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasParentInInstance)
|
||||
{
|
||||
sceneObjects->At(i) = nullptr;
|
||||
LOG(Warning, "Scene object {0} {1} has invalid parent object after load. Removing it.", obj->GetID(), obj->ToString());
|
||||
obj->DeleteObject();
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Link objects to prefab (only deserialized from prefab data)
|
||||
|
||||
@@ -362,6 +362,6 @@ void Scene::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
@@ -204,6 +204,8 @@ void SceneObjectsFactory::SynchronizePrefabInstances(Array<SceneObject*>& sceneO
|
||||
{
|
||||
PROFILE_CPU_NAMED("SynchronizePrefabInstances");
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping);
|
||||
|
||||
// Check all objects with prefab linkage for moving to a proper parent
|
||||
const int32 objectsToCheckCount = sceneObjects.Count();
|
||||
for (int32 i = 0; i < objectsToCheckCount; i++)
|
||||
@@ -257,6 +259,16 @@ void SceneObjectsFactory::SynchronizePrefabInstances(Array<SceneObject*>& sceneO
|
||||
// Reparent
|
||||
obj->SetParent(actualParent, false);
|
||||
}
|
||||
|
||||
// Preserve order in parent (values from prefab are used)
|
||||
if (i != 0)
|
||||
{
|
||||
const auto defaultInstance = prefab->GetDefaultInstance(obj->GetPrefabObjectID());
|
||||
if (defaultInstance)
|
||||
{
|
||||
obj->SetOrderInParent(defaultInstance->GetOrderInParent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check all actors with prefab linkage for adding missing objects
|
||||
@@ -305,6 +317,7 @@ void SceneObjectsFactory::SynchronizePrefabInstances(Array<SceneObject*>& sceneO
|
||||
continue;
|
||||
|
||||
// Create instance (including all children)
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping);
|
||||
SynchronizeNewPrefabInstance(prefab, actor, prefabObjectId, sceneObjects, modifier);
|
||||
}
|
||||
}
|
||||
@@ -313,8 +326,19 @@ void SceneObjectsFactory::SynchronizePrefabInstances(Array<SceneObject*>& sceneO
|
||||
for (int32 i = objectsToCheckCount; i < sceneObjects.Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects[i];
|
||||
|
||||
// Preserve order in parent (values from prefab are used)
|
||||
auto prefab = Content::LoadAsync<Prefab>(obj->GetPrefabID());
|
||||
const auto defaultInstance = prefab && prefab->IsLoaded() ? prefab->GetDefaultInstance(obj->GetPrefabObjectID()) : nullptr;
|
||||
if (defaultInstance)
|
||||
{
|
||||
obj->SetOrderInParent(defaultInstance->GetOrderInParent());
|
||||
}
|
||||
|
||||
obj->PostLoad();
|
||||
}
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::HandleObjectDeserializationError(const ISerializable::DeserializeStream& value)
|
||||
@@ -414,6 +438,7 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstance(Prefab* prefab, Actor* ac
|
||||
|
||||
// Map prefab object ID to the new prefab object instance
|
||||
modifier->IdsMapping[prefabObjectId] = Guid::New();
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping);
|
||||
|
||||
// Create prefab instance (recursive prefab loading to support nested prefabs)
|
||||
auto child = Spawn(*(ISerializable::DeserializeStream*)prefabData, modifier);
|
||||
|
||||
@@ -1016,6 +1016,8 @@ void NavMeshBuilder::Update()
|
||||
{
|
||||
NavBuildQueue.RemoveAt(i--);
|
||||
const auto scene = req.Scene.Get();
|
||||
if (!scene)
|
||||
continue;
|
||||
|
||||
// Early out if scene has no bounds volumes to define nav mesh area
|
||||
if (scene->NavigationVolumes.IsEmpty())
|
||||
|
||||
@@ -72,7 +72,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* nod
|
||||
case 3:
|
||||
value = matrix.GetRow4();
|
||||
break;
|
||||
default: CRASH;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -306,7 +306,7 @@ void ParticleEffect::UpdateBounds()
|
||||
// Empty bounds if there is no particle system to play or it has been never played
|
||||
if (bounds == BoundingBox::Empty)
|
||||
{
|
||||
bounds = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
bounds = BoundingBox(_transform.Translation);
|
||||
}
|
||||
|
||||
_box = bounds;
|
||||
|
||||
@@ -61,7 +61,7 @@ void PhysicsActor::UpdateBounds()
|
||||
}
|
||||
else
|
||||
{
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -71,7 +71,7 @@ void PhysicsActor::UpdateBounds()
|
||||
}
|
||||
else
|
||||
{
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
}
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
|
||||
@@ -178,6 +178,6 @@ void SplineRopeBody::OnTransformChanged()
|
||||
{
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ void CharacterController::UpdateBounds()
|
||||
if (actor)
|
||||
_box = P2C(actor->getWorldBounds(boundsScale));
|
||||
else
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ void CharacterController::OnTransformChanged()
|
||||
}
|
||||
else if (!_controller)
|
||||
{
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ void SplineCollider::OnSplineUpdated()
|
||||
{
|
||||
if (!_spline || !IsActiveInHierarchy() || _spline->GetSplinePointsCount() < 2 || !CollisionData || !CollisionData->IsLoaded())
|
||||
{
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
return;
|
||||
}
|
||||
@@ -178,7 +178,7 @@ void SplineCollider::UpdateBounds()
|
||||
void SplineCollider::GetGeometry(PxGeometryHolder& geometry)
|
||||
{
|
||||
// Reset bounds
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
|
||||
// Skip if sth is missing
|
||||
|
||||
@@ -327,7 +327,7 @@ void Joint::OnTransformChanged()
|
||||
|
||||
// TODO: this could track only local transform changed
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
|
||||
if (_joint)
|
||||
|
||||
@@ -57,7 +57,7 @@ bool NetworkBase::IsReadable(NetworkSocket& socket)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkBase::IsWriteable(NetworkSocket& socket)
|
||||
bool NetworkBase::IsWritable(NetworkSocket& socket)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -183,11 +183,11 @@ public:
|
||||
/// <summary>
|
||||
/// Accepts a pending connection.
|
||||
/// </summary>
|
||||
/// <param name="serverSock">The socket.</param>
|
||||
/// <param name="newSock">The newly connected socket.</param>
|
||||
/// <param name="serverSocket">The socket.</param>
|
||||
/// <param name="newSocket">The newly connected socket.</param>
|
||||
/// <param name="newEndPoint">The end point of the new socket.</param>
|
||||
/// <returns>Returns true on error, otherwise false.</returns>
|
||||
static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint);
|
||||
static bool Accept(NetworkSocket& serverSocket, NetworkSocket& newSocket, NetworkEndPoint& newEndPoint);
|
||||
|
||||
/// <summary>
|
||||
/// Checks for socket readability.
|
||||
@@ -201,7 +201,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="socket">The socket.</param>
|
||||
/// <returns>Returns true when data can be written. Otherwise false.</returns>
|
||||
static bool IsWriteable(NetworkSocket& socket);
|
||||
static bool IsWritable(NetworkSocket& socket);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a socket group. It allocate memory based on the desired capacity.
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#elif PLATFORM_UWP
|
||||
#include "Win32/Win32Network.h"
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Base/NetworkBase.h"
|
||||
#include "Unix/UnixNetwork.h"
|
||||
#elif PLATFORM_PS4
|
||||
#include "Base/NetworkBase.h"
|
||||
#elif PLATFORM_XBOX_SCARLETT
|
||||
#include "Win32/Win32Network.h"
|
||||
#elif PLATFORM_ANDROID
|
||||
#include "Base/NetworkBase.h"
|
||||
#include "Unix/UnixNetwork.h"
|
||||
#elif PLATFORM_SWITCH
|
||||
#include "Base/NetworkBase.h"
|
||||
#else
|
||||
|
||||
@@ -68,8 +68,8 @@ class LinuxThread;
|
||||
typedef LinuxThread Thread;
|
||||
class LinuxWindow;
|
||||
typedef LinuxWindow Window;
|
||||
class NetworkBase;
|
||||
typedef NetworkBase Network;
|
||||
class UnixNetwork;
|
||||
typedef UnixNetwork Network;
|
||||
|
||||
#elif PLATFORM_PS4
|
||||
|
||||
@@ -137,8 +137,8 @@ class AndroidThread;
|
||||
typedef AndroidThread Thread;
|
||||
class AndroidWindow;
|
||||
typedef AndroidWindow Window;
|
||||
class NetworkBase;
|
||||
typedef NetworkBase Network;
|
||||
class UnixNetwork;
|
||||
typedef UnixNetwork Network;
|
||||
|
||||
#elif PLATFORM_SWITCH
|
||||
|
||||
|
||||
405
Source/Engine/Platform/Unix/UnixNetwork.cpp
Normal file
405
Source/Engine/Platform/Unix/UnixNetwork.cpp
Normal file
@@ -0,0 +1,405 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_UNIX
|
||||
|
||||
#include "UnixNetwork.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <cerrno>
|
||||
|
||||
struct UnixSocketData
|
||||
{
|
||||
int sockfd;
|
||||
};
|
||||
static_assert(sizeof(NetworkSocket::Data) >= sizeof(UnixSocketData), "NetworkSocket::Data is not big enough to contains UnixSocketData !");
|
||||
static_assert(sizeof(NetworkEndPoint::Data) >= sizeof(sockaddr_in6), "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !");
|
||||
|
||||
static int GetAddrSize(const sockaddr& addr)
|
||||
{
|
||||
return addr.sa_family == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
|
||||
}
|
||||
|
||||
static int GetAddrSizeFromEP(NetworkEndPoint& endPoint)
|
||||
{
|
||||
return endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
|
||||
}
|
||||
|
||||
static NetworkIPVersion GetIPVersionFromAddr(const sockaddr& addr)
|
||||
{
|
||||
return addr.sa_family == AF_INET6 ? NetworkIPVersion::IPv6 : NetworkIPVersion::IPv4;;
|
||||
}
|
||||
|
||||
static void TranslateSockOptToNative(NetworkSocketOption option, int32* level, int32* name)
|
||||
{
|
||||
switch (option)
|
||||
{
|
||||
#define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) case OPTENUM: *level = OPTLEVEL; *name = OPTNAME; break;
|
||||
SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG)
|
||||
SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR)
|
||||
SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE)
|
||||
SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE)
|
||||
SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST)
|
||||
#ifdef SO_USELOOPBACK
|
||||
SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK)
|
||||
#endif
|
||||
SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER)
|
||||
SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE)
|
||||
SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF)
|
||||
SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF)
|
||||
SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO)
|
||||
SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO)
|
||||
SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR)
|
||||
#ifdef TCP_NODELAY
|
||||
SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY)
|
||||
#endif
|
||||
SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY)
|
||||
SOCKOPT(NetworkSocketOption::Mtu, IPPROTO_IP, IP_MTU)
|
||||
SOCKOPT(NetworkSocketOption::Type, SOL_SOCKET, SO_TYPE)
|
||||
#undef SOCKOPT
|
||||
default:
|
||||
*level = 0;
|
||||
*name = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint)
|
||||
{
|
||||
uint32 size = GetAddrSize(*addr);
|
||||
uint16 port;
|
||||
void* paddr;
|
||||
if (addr->sa_family == AF_INET6)
|
||||
{
|
||||
paddr = &((sockaddr_in6*)addr)->sin6_addr;
|
||||
port = ntohs(((sockaddr_in6*)addr)->sin6_port);
|
||||
}
|
||||
else if (addr->sa_family == AF_INET)
|
||||
{
|
||||
paddr = &((sockaddr_in*)addr)->sin_addr;
|
||||
port = ntohs(((sockaddr_in*)addr)->sin_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Error, "Unable to create endpoint, sockaddr must be INET or INET6! Family : {0}", addr->sa_family);
|
||||
return true;
|
||||
}
|
||||
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
if (inet_ntop(addr->sa_family, paddr, ip, INET6_ADDRSTRLEN) == nullptr)
|
||||
{
|
||||
LOG(Error, "Unable to extract address from sockaddr!");
|
||||
LOG_UNIX_LAST_ERROR;
|
||||
return true;
|
||||
}
|
||||
char strPort[6];
|
||||
sprintf(strPort, "%d", port);
|
||||
endPoint.IPVersion = GetIPVersionFromAddr(*addr);
|
||||
memcpy(endPoint.Data, addr, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv)
|
||||
{
|
||||
socket.Protocol = proto;
|
||||
socket.IPVersion = ipv;
|
||||
const int domain = socket.IPVersion == NetworkIPVersion::IPv6 ? AF_INET6 : AF_INET;
|
||||
const int type = socket.Protocol == NetworkProtocol::Tcp ? SOCK_STREAM : SOCK_DGRAM;
|
||||
const int protocol = socket.Protocol == NetworkProtocol::Tcp ? IPPROTO_TCP : IPPROTO_UDP;
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
sock.sockfd = ::socket(domain, type, protocol);
|
||||
if (sock.sockfd < 0)
|
||||
{
|
||||
LOG(Error, "Can't create native socket");
|
||||
LOG_UNIX_LAST_ERROR;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::DestroySocket(NetworkSocket& socket)
|
||||
{
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
::close(sock.sockfd);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value)
|
||||
{
|
||||
const int32 v = value;
|
||||
return SetSocketOption(socket, option, v);
|
||||
}
|
||||
|
||||
bool UnixNetwork::SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value)
|
||||
{
|
||||
int32 optlvl = 0;
|
||||
int32 optnme = 0;
|
||||
TranslateSockOptToNative(option, &optlvl, &optnme);
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
if (setsockopt(sock.sockfd, optlvl, optnme, (char*)&value, sizeof(value)) == -1)
|
||||
{
|
||||
LOG(Warning, "Unable to set socket option ! Socket : {0}", sock.sockfd);
|
||||
LOG_UNIX_LAST_ERROR;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value)
|
||||
{
|
||||
int32 v;
|
||||
const bool status = GetSocketOption(socket, option, &v);
|
||||
*value = v == 1 ? true : false;
|
||||
return status;
|
||||
}
|
||||
|
||||
bool UnixNetwork::GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value)
|
||||
{
|
||||
int32 optlvl = 0;
|
||||
int32 optnme = 0;
|
||||
TranslateSockOptToNative(option, &optlvl, &optnme);
|
||||
socklen_t size;
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
if (getsockopt(sock.sockfd, optlvl, optnme, (char*)value, &size) == -1)
|
||||
{
|
||||
LOG(Warning, "Unable to get socket option ! Socket : {0}", sock.sockfd);
|
||||
LOG_UNIX_LAST_ERROR;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint)
|
||||
{
|
||||
const uint16 size = GetAddrSizeFromEP(endPoint);
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
if (connect(sock.sockfd, (const sockaddr*)endPoint.Data, size) == -1)
|
||||
{
|
||||
LOG(Error, "Unable to connect socket to address! Socket : {0}", sock.sockfd);
|
||||
LOG_UNIX_LAST_ERROR;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint)
|
||||
{
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
if (socket.IPVersion != endPoint.IPVersion)
|
||||
{
|
||||
LOG(Error, "Can't bind socket to end point, Socket.IPVersion != EndPoint.IPVersion! Socket : {0}", sock.sockfd);
|
||||
return true;
|
||||
}
|
||||
const uint16 size = GetAddrSizeFromEP(endPoint);
|
||||
if (bind(sock.sockfd, (const sockaddr*)endPoint.Data, size) == -1)
|
||||
{
|
||||
LOG(Error, "Unable to bind socket! Socket : {0}", sock.sockfd);
|
||||
LOG_UNIX_LAST_ERROR;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::Listen(NetworkSocket& socket, uint16 queueSize)
|
||||
{
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
if (listen(sock.sockfd, (int32)queueSize) == -1)
|
||||
{
|
||||
LOG(Error, "Unable to listen ! Socket : {0}", sock.sockfd);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::Accept(NetworkSocket& serverSocket, NetworkSocket& newSocket, NetworkEndPoint& newEndPoint)
|
||||
{
|
||||
auto& serverSock = *(UnixSocketData*)&serverSocket.Data;
|
||||
if (serverSocket.Protocol != NetworkProtocol::Tcp)
|
||||
{
|
||||
LOG(Warning, "Can't accept connection on UDP socket! Socket : {0}", serverSock.sockfd);
|
||||
return true;
|
||||
}
|
||||
sockaddr_in6 addr;
|
||||
socklen_t size = sizeof(sockaddr_in6);
|
||||
int sock = accept(serverSock.sockfd, (sockaddr*)&addr, &size);
|
||||
if (sock < 0)
|
||||
{
|
||||
LOG(Warning, "Unable to accept incoming connection! Socket : {0}", serverSock.sockfd);
|
||||
LOG_UNIX_LAST_ERROR;
|
||||
return true;
|
||||
}
|
||||
auto& newSock = *(UnixSocketData*)&newSocket.Data;
|
||||
newSock.sockfd = sock;
|
||||
memcpy(newEndPoint.Data, &addr, size);
|
||||
newSocket.Protocol = serverSocket.Protocol;
|
||||
newSocket.IPVersion = serverSocket.IPVersion;
|
||||
if (CreateEndPointFromAddr((sockaddr*)&addr, newEndPoint))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnixNetwork::IsReadable(NetworkSocket& socket)
|
||||
{
|
||||
return NetworkBase::IsReadable(socket); // TODO: impl this
|
||||
}
|
||||
|
||||
bool UnixNetwork::IsWritable(NetworkSocket& socket)
|
||||
{
|
||||
return NetworkBase::IsWritable(socket); // TODO: impl this
|
||||
}
|
||||
|
||||
bool UnixNetwork::CreateSocketGroup(uint32 capacity, NetworkSocketGroup& group)
|
||||
{
|
||||
return NetworkBase::CreateSocketGroup(capacity, group); // TODO: impl this
|
||||
}
|
||||
|
||||
bool UnixNetwork::DestroySocketGroup(NetworkSocketGroup& group)
|
||||
{
|
||||
return NetworkBase::DestroySocketGroup(group); // TODO: impl this
|
||||
}
|
||||
|
||||
int32 UnixNetwork::Poll(NetworkSocketGroup& group)
|
||||
{
|
||||
return NetworkBase::Poll(group); // TODO: impl this
|
||||
}
|
||||
|
||||
bool UnixNetwork::GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state)
|
||||
{
|
||||
return NetworkBase::GetSocketState(group, index, state); // TODO: impl this
|
||||
}
|
||||
|
||||
int32 UnixNetwork::AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket)
|
||||
{
|
||||
return NetworkBase::AddSocketToGroup(group, socket); // TODO: impl this
|
||||
}
|
||||
|
||||
bool UnixNetwork::GetSocketFromGroup(NetworkSocketGroup& group, uint32 index, NetworkSocket* socket)
|
||||
{
|
||||
return NetworkBase::GetSocketFromGroup(group, index, socket); // TODO: impl this
|
||||
}
|
||||
|
||||
void UnixNetwork::RemoveSocketFromGroup(NetworkSocketGroup& group, uint32 index)
|
||||
{
|
||||
NetworkBase::RemoveSocketFromGroup(group, index); // TODO: impl this
|
||||
}
|
||||
|
||||
bool UnixNetwork::RemoveSocketFromGroup(NetworkSocketGroup& group, NetworkSocket& socket)
|
||||
{
|
||||
return NetworkBase::RemoveSocketFromGroup(group, socket); // TODO: impl this
|
||||
}
|
||||
|
||||
void UnixNetwork::ClearGroup(NetworkSocketGroup& group)
|
||||
{
|
||||
NetworkBase::ClearGroup(group); // TODO: impl this
|
||||
}
|
||||
|
||||
int32 UnixNetwork::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint)
|
||||
{
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
if (endPoint != nullptr && socket.IPVersion != endPoint->IPVersion)
|
||||
{
|
||||
LOG(Error, "Unable to send data, Socket.IPVersion != EndPoint.IPVersion! Socket : {0}", sock.sockfd);
|
||||
return -1;
|
||||
}
|
||||
uint32 size;
|
||||
if (endPoint == nullptr && socket.Protocol == NetworkProtocol::Tcp)
|
||||
{
|
||||
if ((size = send(sock.sockfd, (const char*)data, length, 0)) == -1)
|
||||
{
|
||||
LOG(Error, "Unable to send data! Socket : {0} Data Length : {1}", sock.sockfd, length);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (endPoint != nullptr && socket.Protocol == NetworkProtocol::Udp)
|
||||
{
|
||||
if ((size = sendto(sock.sockfd, (const char*)data, length, 0, (const sockaddr*)endPoint->Data, GetAddrSizeFromEP(*endPoint))) == -1)
|
||||
{
|
||||
LOG(Error, "Unable to send data! Socket : {0} Data Length : {1}", sock.sockfd, length);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: better explanation
|
||||
LOG(Error, "Unable to send data! Socket : {0} Data Length : {1}", sock.sockfd, length);
|
||||
return -1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int32 UnixNetwork::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint)
|
||||
{
|
||||
auto& sock = *(UnixSocketData*)&socket.Data;
|
||||
uint32 size;
|
||||
if (endPoint == nullptr)
|
||||
{
|
||||
if ((size = recv(sock.sockfd, (char*)buffer, bufferSize, 0)) == -1)
|
||||
{
|
||||
LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1}", sock.sockfd, bufferSize);
|
||||
LOG_UNIX_LAST_ERROR;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
socklen_t addrsize = sizeof(sockaddr_in6);
|
||||
sockaddr_in6 addr;
|
||||
if ((size = recvfrom(sock.sockfd, (void*)buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == -1)
|
||||
{
|
||||
LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1}", sock.sockfd, bufferSize);
|
||||
return -1;
|
||||
}
|
||||
if (CreateEndPointFromAddr((sockaddr*)&addr, *endPoint))
|
||||
return true;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool UnixNetwork::CreateEndPoint(NetworkAddress& address, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable)
|
||||
{
|
||||
int status;
|
||||
addrinfo hints;
|
||||
addrinfo* info;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = ipv == NetworkIPVersion::IPv6 ? AF_INET6 : ipv == NetworkIPVersion::IPv4 ? AF_INET : AF_UNSPEC;
|
||||
hints.ai_flags |= AI_ADDRCONFIG;
|
||||
hints.ai_flags |= AI_V4MAPPED;
|
||||
if (bindable)
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
// consider using NUMERICHOST/NUMERICSERV if address is a valid Ipv4 or IPv6 so we can skip some look up ( potentially slow when resolving host names )
|
||||
const StringAsANSI<60> addressAnsi(*address.Address, address.Address.Length());
|
||||
const StringAsANSI<10> portAnsi(*address.Port, address.Port.Length());
|
||||
if ((status = getaddrinfo(address.Address == String::Empty ? nullptr : addressAnsi.Get(), address.Port == String::Empty ? nullptr : portAnsi.Get(), &hints, &info)) != 0)
|
||||
{
|
||||
LOG(Error, "Unable to query info for address : {0} Error : {1}", address.Address != String::Empty ? *address.Address : TEXT("ANY"), String(gai_strerror(status)));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info == nullptr)
|
||||
{
|
||||
LOG(Error, "Unable to resolve address! Address : {0}", address.Address != String::Empty ? *address.Address : TEXT("ANY"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CreateEndPointFromAddr(info->ai_addr, endPoint))
|
||||
{
|
||||
freeaddrinfo(info);
|
||||
return true;
|
||||
}
|
||||
freeaddrinfo(info);
|
||||
return false;
|
||||
}
|
||||
|
||||
NetworkEndPoint UnixNetwork::RemapEndPointToIPv6(NetworkEndPoint endPoint)
|
||||
{
|
||||
return NetworkBase::RemapEndPointToIPv6(endPoint); // TODO: impl this
|
||||
}
|
||||
|
||||
#endif
|
||||
41
Source/Engine/Platform/Unix/UnixNetwork.h
Normal file
41
Source/Engine/Platform/Unix/UnixNetwork.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_UNIX
|
||||
|
||||
#include "Engine/Platform/Base/NetworkBase.h"
|
||||
|
||||
class FLAXENGINE_API UnixNetwork : public NetworkBase
|
||||
{
|
||||
public:
|
||||
|
||||
// [NetworkBase]
|
||||
static bool CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv);
|
||||
static bool DestroySocket(NetworkSocket& socket);
|
||||
static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value);
|
||||
static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value);
|
||||
static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value);
|
||||
static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value);
|
||||
static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint);
|
||||
static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint);
|
||||
static bool Listen(NetworkSocket& socket, uint16 queueSize);
|
||||
static bool Accept(NetworkSocket& serverSocket, NetworkSocket& newSocket, NetworkEndPoint& newEndPoint);
|
||||
static bool IsReadable(NetworkSocket& socket);
|
||||
static bool IsWritable(NetworkSocket& socket);
|
||||
static bool CreateSocketGroup(uint32 capacity, NetworkSocketGroup& group);
|
||||
static bool DestroySocketGroup(NetworkSocketGroup& group);
|
||||
static int32 Poll(NetworkSocketGroup& group);
|
||||
static bool GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state);
|
||||
static int32 AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket);
|
||||
static bool GetSocketFromGroup(NetworkSocketGroup& group, uint32 index, NetworkSocket* socket);
|
||||
static void RemoveSocketFromGroup(NetworkSocketGroup& group, uint32 index);
|
||||
static bool RemoveSocketFromGroup(NetworkSocketGroup& group, NetworkSocket& socket);
|
||||
static void ClearGroup(NetworkSocketGroup& group);
|
||||
static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr);
|
||||
static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr);
|
||||
static bool CreateEndPoint(NetworkAddress& address, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = true);
|
||||
static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint endPoint);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_WIN32
|
||||
|
||||
#include "Win32Network.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
@@ -7,10 +9,8 @@
|
||||
#include <WS2ipdef.h>
|
||||
#include <WS2tcpip.h>
|
||||
|
||||
#define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) case OPTENUM: *level = OPTLEVEL; *name = OPTNAME; break;
|
||||
|
||||
static_assert(sizeof NetworkSocket::Data >= sizeof SOCKET, "NetworkSocket::Data is not big enough to contains SOCKET !");
|
||||
static_assert(sizeof NetworkEndPoint::Data >= sizeof sockaddr_in6, "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !");
|
||||
static_assert(sizeof(NetworkSocket::Data) >= sizeof(SOCKET), "NetworkSocket::Data is not big enough to contains SOCKET !");
|
||||
static_assert(sizeof(NetworkEndPoint::Data) >= sizeof(sockaddr_in6), "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !");
|
||||
static_assert(SOCKGROUP_ITEMSIZE >= sizeof(pollfd), "SOCKGROUP_ITEMSIZE macro is not big enough to contains pollfd !");
|
||||
|
||||
// @formatter:off
|
||||
@@ -44,12 +44,12 @@ static String GetLastErrorMessage()
|
||||
|
||||
static int GetAddrSize(const sockaddr& addr)
|
||||
{
|
||||
return addr.sa_family == AF_INET6 ? sizeof sockaddr_in6 : sizeof sockaddr_in;
|
||||
return addr.sa_family == AF_INET6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
|
||||
}
|
||||
|
||||
static int GetAddrSizeFromEP(NetworkEndPoint& endPoint)
|
||||
{
|
||||
return endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof sockaddr_in6 : sizeof sockaddr_in;
|
||||
return endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
|
||||
}
|
||||
|
||||
static NetworkIPVersion GetIPVersionFromAddr(const sockaddr& addr)
|
||||
@@ -118,6 +118,7 @@ static void TranslateSockOptToNative(NetworkSocketOption option, int32* level, i
|
||||
{
|
||||
switch (option)
|
||||
{
|
||||
#define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) case OPTENUM: *level = OPTLEVEL; *name = OPTNAME; break;
|
||||
SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG)
|
||||
SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR)
|
||||
SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE)
|
||||
@@ -135,6 +136,11 @@ static void TranslateSockOptToNative(NetworkSocketOption option, int32* level, i
|
||||
SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY)
|
||||
SOCKOPT(NetworkSocketOption::Mtu, IPPROTO_IP, IP_MTU)
|
||||
SOCKOPT(NetworkSocketOption::Type, SOL_SOCKET, SO_TYPE)
|
||||
#undef SOCKOPT
|
||||
default:
|
||||
*level = 0;
|
||||
*name = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +158,7 @@ bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkProtocol proto, Ne
|
||||
LOG(Error, "Can't create native socket! Error : {0}", GetLastErrorMessage());
|
||||
return true;
|
||||
}
|
||||
memcpy(socket.Data, &sock, sizeof sock);
|
||||
memcpy(socket.Data, &sock, sizeof(sock));
|
||||
unsigned long value = 1;
|
||||
if (ioctlsocket(sock, FIONBIO, &value) == SOCKET_ERROR)
|
||||
{
|
||||
@@ -187,7 +193,7 @@ bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption op
|
||||
|
||||
TranslateSockOptToNative(option, &optlvl, &optnme);
|
||||
|
||||
if (setsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)&value, sizeof value) == SOCKET_ERROR)
|
||||
if (setsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)&value, sizeof(value)) == SOCKET_ERROR)
|
||||
{
|
||||
LOG(Warning, "Unable to set socket option ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage());
|
||||
return true;
|
||||
@@ -241,7 +247,7 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint)
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint16 size = endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof sockaddr_in6 : sizeof sockaddr_in;
|
||||
const uint16 size = GetAddrSizeFromEP(endPoint);
|
||||
if (bind(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR)
|
||||
{
|
||||
LOG(Error, "Unable to bind socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage());
|
||||
@@ -260,28 +266,28 @@ bool Win32Network::Listen(NetworkSocket& socket, uint16 queueSize)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint)
|
||||
bool Win32Network::Accept(NetworkSocket& serverSocket, NetworkSocket& newSocket, NetworkEndPoint& newEndPoint)
|
||||
{
|
||||
if (serverSock.Protocol != NetworkProtocol::Tcp)
|
||||
if (serverSocket.Protocol != NetworkProtocol::Tcp)
|
||||
{
|
||||
LOG(Warning, "Can't accept connection on UDP socket! Socket : {0}", *(SOCKET*)serverSock.Data);
|
||||
LOG(Warning, "Can't accept connection on UDP socket! Socket : {0}", *(SOCKET*)serverSocket.Data);
|
||||
return true;
|
||||
}
|
||||
SOCKET sock;
|
||||
sockaddr_in6 addr;
|
||||
int32 size = sizeof sockaddr_in6;
|
||||
if ((sock = accept(*(SOCKET*)serverSock.Data, (sockaddr*)&addr, &size)) == INVALID_SOCKET)
|
||||
int32 size = sizeof(sockaddr_in6);
|
||||
if ((sock = accept(*(SOCKET*)serverSocket.Data, (sockaddr*)&addr, &size)) == INVALID_SOCKET)
|
||||
{
|
||||
int32 error = WSAGetLastError();
|
||||
if (error == WSAEWOULDBLOCK)
|
||||
return false;
|
||||
LOG(Warning, "Unable to accept incoming connection! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetErrorMessage(error));
|
||||
LOG(Warning, "Unable to accept incoming connection! Socket : {0} Error : {1}", *(SOCKET*)serverSocket.Data, GetErrorMessage(error));
|
||||
return true;
|
||||
}
|
||||
memcpy(newSock.Data, &sock, sizeof sock);
|
||||
memcpy(newSocket.Data, &sock, sizeof(sock));
|
||||
memcpy(newEndPoint.Data, &addr, size);
|
||||
newSock.Protocol = serverSock.Protocol;
|
||||
newSock.IPVersion = serverSock.IPVersion;
|
||||
newSocket.Protocol = serverSocket.Protocol;
|
||||
newSocket.IPVersion = serverSocket.IPVersion;
|
||||
if (CreateEndPointFromAddr((sockaddr*)&addr, newEndPoint))
|
||||
return true;
|
||||
return false;
|
||||
@@ -305,7 +311,7 @@ bool Win32Network::IsReadable(NetworkSocket& socket)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Win32Network::IsWriteable(NetworkSocket& socket)
|
||||
bool Win32Network::IsWritable(NetworkSocket& socket)
|
||||
{
|
||||
pollfd entry;
|
||||
entry.fd = *(SOCKET*)socket.Data;
|
||||
@@ -358,7 +364,7 @@ bool Win32Network::GetSocketState(NetworkSocketGroup& group, uint32 index, Netwo
|
||||
if (index >= group.Capacity)
|
||||
return true;
|
||||
pollfd* pollptr = (pollfd*)&group.Data[index * SOCKGROUP_ITEMSIZE];
|
||||
memset(&state, 0, sizeof state);
|
||||
memset(&state, 0, sizeof(state));
|
||||
if (pollptr->revents & POLLERR)
|
||||
state.Error = true;
|
||||
if (pollptr->revents & POLLHUP)
|
||||
@@ -398,7 +404,7 @@ bool Win32Network::GetSocketFromGroup(NetworkSocketGroup& group, uint32 index, N
|
||||
if (index >= group.Capacity)
|
||||
return true;
|
||||
SOCKET s = ((pollfd*)&group.Data[index * SOCKGROUP_ITEMSIZE])->fd;
|
||||
memcpy(socket->Data, &s, sizeof s);
|
||||
memcpy(socket->Data, &s, sizeof(s));
|
||||
int32 value;
|
||||
if (GetSocketOption(*socket, NetworkSocketOption::Type, &value))
|
||||
return true;
|
||||
@@ -490,7 +496,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 addrsize = sizeof sockaddr_in6;
|
||||
int32 addrsize = sizeof(sockaddr_in6);
|
||||
sockaddr_in6 addr;
|
||||
if ((size = recvfrom(*(SOCKET*)socket.Data, (char*)buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR)
|
||||
{
|
||||
@@ -508,7 +514,7 @@ bool Win32Network::CreateEndPoint(NetworkAddress& address, NetworkIPVersion ipv,
|
||||
int status;
|
||||
addrinfoW hints;
|
||||
addrinfoW* info;
|
||||
memset(&hints, 0, sizeof hints);
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = ipv == NetworkIPVersion::IPv6 ? AF_INET6 : ipv == NetworkIPVersion::IPv4 ? AF_INET : AF_UNSPEC;
|
||||
hints.ai_flags |= AI_ADDRCONFIG;
|
||||
hints.ai_flags |= AI_V4MAPPED;
|
||||
@@ -518,13 +524,13 @@ bool Win32Network::CreateEndPoint(NetworkAddress& address, NetworkIPVersion ipv,
|
||||
// consider using NUMERICHOST/NUMERICSERV if address is a valid Ipv4 or IPv6 so we can skip some look up ( potentially slow when resolving host names )
|
||||
if ((status = GetAddrInfoW(address.Address == String::Empty ? nullptr : address.Address.Get(), address.Port == String::Empty ? nullptr : address.Port.Get(), &hints, &info)) != 0)
|
||||
{
|
||||
LOG(Error, "Unable to query info for address : {0} Error : {1}", address.Address != String::Empty ? address.Address : String("ANY"), gai_strerror(status));
|
||||
LOG(Error, "Unable to query info for address : {0} Error : {1}", address.Address != String::Empty ? *address.Address : TEXT("ANY"), gai_strerror(status));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info == nullptr)
|
||||
{
|
||||
LOG(Error, "Unable to resolve address! Address : {0}", address.Address != String::Empty ? address.Address : String("ANY"));
|
||||
LOG(Error, "Unable to resolve address! Address : {0}", address.Address != String::Empty ? *address.Address : TEXT("ANY"));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -534,7 +540,6 @@ bool Win32Network::CreateEndPoint(NetworkAddress& address, NetworkIPVersion ipv,
|
||||
return true;
|
||||
}
|
||||
FreeAddrInfoW(info);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -552,7 +557,7 @@ NetworkEndPoint Win32Network::RemapEndPointToIPv6(NetworkEndPoint endPoint)
|
||||
const SCOPE_ID scope = SCOPEID_UNSPECIFIED_INIT;
|
||||
|
||||
// Can be replaced by windows built-in macro IN6ADDR_SETV4MAPPED()
|
||||
memset(addr6, 0, sizeof sockaddr_in6);
|
||||
memset(addr6, 0, sizeof(sockaddr_in6));
|
||||
addr6->sin6_family = AF_INET6;
|
||||
addr6->sin6_scope_struct = scope;
|
||||
addr6->sin6_addr = v4MappedPrefix;
|
||||
@@ -562,3 +567,5 @@ NetworkEndPoint Win32Network::RemapEndPointToIPv6(NetworkEndPoint endPoint)
|
||||
|
||||
return pv6;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
class FLAXENGINE_API Win32Network : public NetworkBase
|
||||
{
|
||||
public:
|
||||
|
||||
// [NetworkBase]
|
||||
static bool CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv);
|
||||
static bool DestroySocket(NetworkSocket& socket);
|
||||
@@ -19,9 +20,9 @@ public:
|
||||
static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint);
|
||||
static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint);
|
||||
static bool Listen(NetworkSocket& socket, uint16 queueSize);
|
||||
static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint);
|
||||
static bool Accept(NetworkSocket& serverSocket, NetworkSocket& newSocket, NetworkEndPoint& newEndPoint);
|
||||
static bool IsReadable(NetworkSocket& socket);
|
||||
static bool IsWriteable(NetworkSocket& socket);
|
||||
static bool IsWritable(NetworkSocket& socket);
|
||||
static bool CreateSocketGroup(uint32 capacity, NetworkSocketGroup& group);
|
||||
static bool DestroySocketGroup(NetworkSocketGroup& group);
|
||||
static int32 Poll(NetworkSocketGroup& group);
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Platform/IGuiData.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#include "Engine/Threading/ThreadPool.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
@@ -630,6 +632,10 @@ DragDropEffect WindowsWindow::DoDragDrop(const StringView& data)
|
||||
dropSource->Release();
|
||||
ReleaseStgMedium(&stgmed);
|
||||
|
||||
// Fix hanging mouse state (Windows doesn't send WM_LBUTTONUP when we end the drag and drop)
|
||||
if (Input::GetMouseButton(MouseButton::Left))
|
||||
Input::Mouse->OnMouseUp(Input::Mouse->GetPosition(), MouseButton::Left, this);
|
||||
|
||||
return SUCCEEDED(result) ? dropEffectFromOleEnum(dwEffect) : DragDropEffect::None;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json.JsonCustomSerializers;
|
||||
using FlaxEngine.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
@@ -119,6 +120,115 @@ namespace FlaxEngine.Json
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize SoftObjectReference as Guid in internal format.
|
||||
/// </summary>
|
||||
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
|
||||
internal class MarginConverter : JsonConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var valueMargin = (Margin)value;
|
||||
|
||||
writer.WriteStartObject();
|
||||
{
|
||||
writer.WritePropertyName("Left");
|
||||
writer.WriteValue(valueMargin.Left);
|
||||
writer.WritePropertyName("Right");
|
||||
writer.WriteValue(valueMargin.Right);
|
||||
writer.WritePropertyName("Top");
|
||||
writer.WriteValue(valueMargin.Top);
|
||||
writer.WritePropertyName("Bottom");
|
||||
writer.WriteValue(valueMargin.Bottom);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteJsonDiff(JsonWriter writer, object value, object other, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var valueMargin = (Margin)value;
|
||||
var otherMargin = (Margin)other;
|
||||
writer.WriteStartObject();
|
||||
if (!Mathf.NearEqual(valueMargin.Left, otherMargin.Left))
|
||||
{
|
||||
writer.WritePropertyName("Left");
|
||||
writer.WriteValue(valueMargin.Left);
|
||||
}
|
||||
if (!Mathf.NearEqual(valueMargin.Right, otherMargin.Right))
|
||||
{
|
||||
writer.WritePropertyName("Right");
|
||||
writer.WriteValue(valueMargin.Right);
|
||||
}
|
||||
if (!Mathf.NearEqual(valueMargin.Top, otherMargin.Top))
|
||||
{
|
||||
writer.WritePropertyName("Top");
|
||||
writer.WriteValue(valueMargin.Top);
|
||||
}
|
||||
if (!Mathf.NearEqual(valueMargin.Bottom, otherMargin.Bottom))
|
||||
{
|
||||
writer.WritePropertyName("Bottom");
|
||||
writer.WriteValue(valueMargin.Bottom);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var value = (Margin?)existingValue ?? new Margin();
|
||||
if (reader.TokenType == JsonToken.StartObject)
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.PropertyName:
|
||||
{
|
||||
var propertyName = (string)reader.Value;
|
||||
var propertyValue = (float)reader.ReadAsDouble();
|
||||
switch (propertyName)
|
||||
{
|
||||
case "Left":
|
||||
value.Left = propertyValue;
|
||||
break;
|
||||
case "Right":
|
||||
value.Right = propertyValue;
|
||||
break;
|
||||
case "Top":
|
||||
value.Top = propertyValue;
|
||||
break;
|
||||
case "Bottom":
|
||||
value.Bottom = propertyValue;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JsonToken.Comment: break;
|
||||
default: return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(Margin);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanRead => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanWrite => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanWriteDiff => true;
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Serialize Guid values using `N` format
|
||||
@@ -219,6 +329,7 @@ namespace FlaxEngine.Json
|
||||
settings.Converters.Add(ObjectConverter);
|
||||
settings.Converters.Add(new SceneReferenceConverter());
|
||||
settings.Converters.Add(new SoftObjectReferenceConverter());
|
||||
settings.Converters.Add(new MarginConverter());
|
||||
settings.Converters.Add(new VersionConverter());
|
||||
//settings.Converters.Add(new GuidConverter());
|
||||
return settings;
|
||||
|
||||
@@ -100,6 +100,8 @@ void ChangeIds(rapidjson_flax::Value& obj, rapidjson_flax::Document& document, c
|
||||
|
||||
void JsonTools::ChangeIds(Document& doc, const Dictionary<Guid, Guid>& mapping)
|
||||
{
|
||||
if (mapping.IsEmpty())
|
||||
return;
|
||||
::ChangeIds(doc, doc, mapping);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Terrain::~Terrain()
|
||||
void Terrain::UpdateBounds()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
for (int32 i = 0; i < _patches.Count(); i++)
|
||||
{
|
||||
auto patch = _patches[i];
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace
|
||||
{
|
||||
// Loaded and parsed features data cache
|
||||
Dictionary<StringAnsi, FeatureData> Features;
|
||||
CriticalSection FeaturesLock;
|
||||
}
|
||||
|
||||
bool FeatureData::Init()
|
||||
@@ -174,6 +175,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
|
||||
features.Add(typeName); \
|
||||
if (!Features.ContainsKey(typeName)) \
|
||||
{ \
|
||||
ScopeLock lock(FeaturesLock); \
|
||||
auto& feature = Features[typeName]; \
|
||||
type::Generate(feature.Data); \
|
||||
if (feature.Init()) \
|
||||
@@ -388,7 +390,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
|
||||
// Update material usage based on material generator outputs
|
||||
materialInfo.UsageFlags = baseLayer->UsageFlags;
|
||||
|
||||
#define WRITE_FEATURES(input) for (auto f : features) _writer.Write(Features[f].Inputs[(int32)FeatureTemplateInputsMapping::input]);
|
||||
#define WRITE_FEATURES(input) FeaturesLock.Lock(); for (auto f : features) _writer.Write(Features[f].Inputs[(int32)FeatureTemplateInputsMapping::input]); FeaturesLock.Unlock();
|
||||
// Defines
|
||||
{
|
||||
_writer.Write(TEXT("#define MATERIAL_MASK_THRESHOLD ({0})\n"), baseLayer->MaskThreshold);
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Border control that draws the border around the control edges (inner and outer sides).
|
||||
/// </summary>
|
||||
public class Border : Control
|
||||
public class Border : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the color used to draw border lines.
|
||||
@@ -30,9 +30,9 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
base.Draw();
|
||||
base.DrawSelf();
|
||||
|
||||
Render2D.DrawRectangle(new Rectangle(Vector2.Zero, Size), BorderColor, BorderWidth);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Button control
|
||||
/// </summary>
|
||||
public class Button : Control
|
||||
public class Button : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The default height fro the buttons.
|
||||
@@ -171,7 +171,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
// Cache data
|
||||
Rectangle clientRect = new Rectangle(Vector2.Zero, Size);
|
||||
|
||||
@@ -80,9 +80,9 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
base.Draw();
|
||||
base.DrawSelf();
|
||||
|
||||
if (Brush == null)
|
||||
return;
|
||||
|
||||
@@ -200,8 +200,8 @@ namespace FlaxEngine.GUI
|
||||
color *= 0.6f;
|
||||
|
||||
var scale = 1.0f;
|
||||
var hAlignment = _autoWidth ? TextAlignment.Near : HorizontalAlignment;
|
||||
var wAlignment = _autoHeight ? TextAlignment.Near : VerticalAlignment;
|
||||
var hAlignment = HorizontalAlignment;
|
||||
var wAlignment = VerticalAlignment;
|
||||
if (_autoFitText)
|
||||
{
|
||||
hAlignment = TextAlignment.Center;
|
||||
@@ -239,7 +239,13 @@ namespace FlaxEngine.GUI
|
||||
if (font)
|
||||
{
|
||||
// Calculate text size
|
||||
_textSize = font.MeasureText(_text);
|
||||
var layout = TextLayoutOptions.Default;
|
||||
layout.TextWrapping = Wrapping;
|
||||
if (_autoHeight && !_autoWidth)
|
||||
layout.Bounds.Size.X = Width - Margin.Width;
|
||||
else if (_autoWidth && !_autoHeight)
|
||||
layout.Bounds.Size.Y = Height - Margin.Height;
|
||||
_textSize = font.MeasureText(_text, ref layout);
|
||||
|
||||
// Check if size is controlled via text
|
||||
if (_autoWidth || _autoHeight)
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace FlaxEngine.GUI
|
||||
/// Progress bar control shows visual progress of the action or set of actions.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||
public class ProgressBar : Control
|
||||
public class ProgressBar : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The value.
|
||||
@@ -160,9 +160,9 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
base.Draw();
|
||||
base.DrawSelf();
|
||||
|
||||
float progressNormalized = (_current - _minimum) / _maximum;
|
||||
if (progressNormalized > 0.001f)
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
// Draw cached texture
|
||||
if (_texture && !_invalid && !_isDuringTextureDraw)
|
||||
@@ -119,7 +119,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
// Draw default UI directly
|
||||
base.Draw();
|
||||
base.DrawSelf();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace FlaxEngine.GUI
|
||||
var font = textBlock.Style.Font.GetFont();
|
||||
if (font)
|
||||
{
|
||||
height = font.Height / Platform.DpiScale;
|
||||
height = font.Height / DpiScale;
|
||||
return textBlock.Bounds.UpperLeft;
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ namespace FlaxEngine.GUI
|
||||
var font = textBlock.Style.Font.GetFont();
|
||||
if (font)
|
||||
{
|
||||
height = font.Height / Platform.DpiScale;
|
||||
height = font.Height / DpiScale;
|
||||
return textBlock.Bounds.UpperRight;
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ namespace FlaxEngine.GUI
|
||||
var font = textBlock.Style.Font.GetFont();
|
||||
if (!font)
|
||||
break;
|
||||
height = font.Height / Platform.DpiScale;
|
||||
height = font.Height / DpiScale;
|
||||
return textBlock.Bounds.Location + font.GetCharPosition(_text, ref textBlock.Range, index - textBlock.Range.StartIndex);
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ namespace FlaxEngine.GUI
|
||||
var font = textBlock.Style.Font.GetFont();
|
||||
if (!font)
|
||||
break;
|
||||
height = font.Height / Platform.DpiScale;
|
||||
height = font.Height / DpiScale;
|
||||
return textBlock.Bounds.UpperRight;
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
// Cache data
|
||||
var rect = new Rectangle(Vector2.Zero, Size);
|
||||
@@ -280,7 +280,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
Vector2 leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : font.GetCharPosition(_text, selection.StartIndex);
|
||||
Vector2 rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : font.GetCharPosition(_text, selection.EndIndex);
|
||||
float height = font.Height / Platform.DpiScale;
|
||||
float height = font.Height / DpiScale;
|
||||
float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f);
|
||||
alpha *= alpha;
|
||||
Color selectionColor = Color.White * alpha;
|
||||
@@ -330,7 +330,7 @@ namespace FlaxEngine.GUI
|
||||
if (textBlock.Style.UnderlineBrush != null)
|
||||
{
|
||||
var underLineHeight = 2.0f;
|
||||
var height = font.Height / Platform.DpiScale;
|
||||
var height = font.Height / DpiScale;
|
||||
var underlineRect = new Rectangle(textBlock.Bounds.Location.X, textBlock.Bounds.Location.Y + height - underLineHeight * 0.5f, textBlock.Bounds.Width, underLineHeight);
|
||||
textBlock.Style.UnderlineBrush.Draw(underlineRect, textBlock.Style.Color);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace FlaxEngine.GUI
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
height = font.Height / Platform.DpiScale;
|
||||
height = font.Height / DpiScale;
|
||||
return font.GetCharPosition(_text, index, ref _layout);
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
// Cache data
|
||||
var rect = new Rectangle(Vector2.Zero, Size);
|
||||
@@ -159,7 +159,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
Vector2 leftEdge = font.GetCharPosition(_text, SelectionLeft, ref _layout);
|
||||
Vector2 rightEdge = font.GetCharPosition(_text, SelectionRight, ref _layout);
|
||||
float fontHeight = font.Height / Platform.DpiScale;
|
||||
float fontHeight = font.Height / DpiScale;
|
||||
|
||||
// Draw selection background
|
||||
float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Base class for all text box controls which can gather text input from the user.
|
||||
/// </summary>
|
||||
public abstract class TextBoxBase : Control
|
||||
public abstract class TextBoxBase : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The text separators (used for words skipping).
|
||||
@@ -539,6 +539,12 @@ namespace FlaxEngine.GUI
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's not selected
|
||||
if (_selectionStart == -1 && _selectionEnd == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rectangle caretBounds = CaretBounds;
|
||||
Rectangle textArea = TextRectangle;
|
||||
|
||||
|
||||
@@ -262,6 +262,8 @@ namespace FlaxEngine.GUI
|
||||
// Change order
|
||||
_children.Insert(newIndex, child);
|
||||
}
|
||||
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -614,25 +616,32 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Draw the control and the children.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
DrawSelf();
|
||||
|
||||
// Push clipping mask
|
||||
if (ClipChildren)
|
||||
{
|
||||
GetDesireClientArea(out var clientArea);
|
||||
Render2D.PushClip(ref clientArea);
|
||||
}
|
||||
|
||||
DrawChildren();
|
||||
|
||||
// Pop clipping mask
|
||||
if (ClipChildren)
|
||||
{
|
||||
DrawChildren();
|
||||
Render2D.PopClip();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawChildren();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the control.
|
||||
/// </summary>
|
||||
public virtual void DrawSelf()
|
||||
{
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -223,57 +223,60 @@ namespace FlaxEngine.GUI
|
||||
set
|
||||
{
|
||||
if (!_bounds.Equals(ref value))
|
||||
{
|
||||
// Calculate anchors based on the parent container client area
|
||||
Margin anchors;
|
||||
if (_parent != null)
|
||||
{
|
||||
_parent.GetDesireClientArea(out var parentBounds);
|
||||
anchors = new Margin
|
||||
(
|
||||
_anchorMin.X * parentBounds.Size.X + parentBounds.Location.X,
|
||||
_anchorMax.X * parentBounds.Size.X,
|
||||
_anchorMin.Y * parentBounds.Size.Y + parentBounds.Location.Y,
|
||||
_anchorMax.Y * parentBounds.Size.Y
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
anchors = Margin.Zero;
|
||||
}
|
||||
|
||||
// Calculate offsets on X axis
|
||||
_offsets.Left = value.Location.X - anchors.Left;
|
||||
if (_anchorMin.X != _anchorMax.X)
|
||||
{
|
||||
_offsets.Right = anchors.Right - value.Location.X - value.Size.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
_offsets.Right = value.Size.X;
|
||||
}
|
||||
|
||||
// Calculate offsets on Y axis
|
||||
_offsets.Top = value.Location.Y - anchors.Top;
|
||||
if (_anchorMin.Y != _anchorMax.Y)
|
||||
{
|
||||
_offsets.Bottom = anchors.Bottom - value.Location.Y - value.Size.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
_offsets.Bottom = value.Size.Y;
|
||||
}
|
||||
|
||||
// Flush the control bounds
|
||||
UpdateBounds();
|
||||
}
|
||||
SetBounds(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBounds(ref Rectangle value)
|
||||
{
|
||||
// Calculate anchors based on the parent container client area
|
||||
Margin anchors;
|
||||
if (_parent != null)
|
||||
{
|
||||
_parent.GetDesireClientArea(out var parentBounds);
|
||||
anchors = new Margin
|
||||
(
|
||||
_anchorMin.X * parentBounds.Size.X + parentBounds.Location.X,
|
||||
_anchorMax.X * parentBounds.Size.X,
|
||||
_anchorMin.Y * parentBounds.Size.Y + parentBounds.Location.Y,
|
||||
_anchorMax.Y * parentBounds.Size.Y
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
anchors = Margin.Zero;
|
||||
}
|
||||
|
||||
// Calculate offsets on X axis
|
||||
_offsets.Left = value.Location.X - anchors.Left;
|
||||
if (_anchorMin.X != _anchorMax.X)
|
||||
{
|
||||
_offsets.Right = anchors.Right - value.Location.X - value.Size.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
_offsets.Right = value.Size.X;
|
||||
}
|
||||
|
||||
// Calculate offsets on Y axis
|
||||
_offsets.Top = value.Location.Y - anchors.Top;
|
||||
if (_anchorMin.Y != _anchorMax.Y)
|
||||
{
|
||||
_offsets.Bottom = anchors.Bottom - value.Location.Y - value.Size.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
_offsets.Bottom = value.Size.Y;
|
||||
}
|
||||
|
||||
// Flush the control bounds
|
||||
UpdateBounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scale.
|
||||
/// Gets or sets the scale. Scales control according to its Pivot which by default is (0.5,0.5) (middle of the control). If you set pivot to (0,0) it will scale the control based on it's upper-left corner.
|
||||
/// </summary>
|
||||
[ExpandGroups, EditorDisplay("Transform"), Limit(float.MinValue, float.MaxValue, 0.1f), EditorOrder(1020), Tooltip("The control scale parameter.")]
|
||||
[ExpandGroups, EditorDisplay("Transform"), Limit(float.MinValue, float.MaxValue, 0.1f), EditorOrder(1020), Tooltip("The control scale parameter. Scales control according to its Pivot which by default is (0.5,0.5) (middle of the control). If you set pivot to (0,0) it will scale the control based on it's upper-left corner.")]
|
||||
public Vector2 Scale
|
||||
{
|
||||
get => _scale;
|
||||
@@ -303,9 +306,9 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shear transform angles (x, y). Defined in degrees.
|
||||
/// Gets or sets the shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.
|
||||
/// </summary>
|
||||
[ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees.")]
|
||||
[ExpandGroups, EditorDisplay("Transform"), EditorOrder(1040), Tooltip("The shear transform angles (x, y). Defined in degrees. Shearing happens relative to the control pivot point.")]
|
||||
public Vector2 Shear
|
||||
{
|
||||
get => _shear;
|
||||
@@ -319,9 +322,9 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation angle (in degrees).
|
||||
/// Gets or sets the rotation angle (in degrees). Control is rotated around it's pivot point (middle of the control by default).
|
||||
/// </summary>
|
||||
[ExpandGroups, EditorDisplay("Transform"), EditorOrder(1050), Tooltip("The control rotation angle (in degrees).")]
|
||||
[ExpandGroups, EditorDisplay("Transform"), EditorOrder(1050), Tooltip("The control rotation angle (in degrees). Control is rotated around it's pivot point (middle of the control by default).")]
|
||||
public float Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
@@ -553,7 +556,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
bounds.Location += parentBounds.Location;
|
||||
}
|
||||
Bounds = bounds;
|
||||
SetBounds(ref bounds);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,7 +209,6 @@ namespace FlaxEngine.GUI
|
||||
public bool Enabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
@@ -255,7 +254,6 @@ namespace FlaxEngine.GUI
|
||||
public bool Visible
|
||||
{
|
||||
get => _isVisible;
|
||||
|
||||
set
|
||||
{
|
||||
if (_isVisible != value)
|
||||
@@ -312,6 +310,11 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public virtual WindowRootControl RootWindow => _root?.RootWindow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control DPI scale factor (1 is default). Includes custom DPI scale.
|
||||
/// </summary>
|
||||
public float DpiScale => _root?.RootWindow?.Window.DpiScale ?? Platform.DpiScale;
|
||||
|
||||
/// <summary>
|
||||
/// Gets screen position of the control (upper left corner).
|
||||
/// </summary>
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public void SyncBackbufferSize()
|
||||
{
|
||||
float scale = ResolutionScale * (RootWindow?.DpiScale ?? Platform.DpiScale);
|
||||
float scale = ResolutionScale * DpiScale;
|
||||
int width = Mathf.CeilToInt(Width * scale);
|
||||
int height = Mathf.CeilToInt(Height * scale);
|
||||
if (_customResolution.HasValue)
|
||||
|
||||
@@ -55,11 +55,6 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public bool IsMaximized => _window.IsMaximized;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the window DPI scale factor (1 is default). Includes custom DPI scale
|
||||
/// </summary>
|
||||
public float DpiScale => _window.DpiScale;
|
||||
|
||||
internal WindowRootControl(Window window)
|
||||
{
|
||||
_window = window;
|
||||
|
||||
@@ -31,7 +31,7 @@ TextRender::TextRender(const SpawnParams& params)
|
||||
{
|
||||
_world = Matrix::Identity;
|
||||
_color = Color::White;
|
||||
_localBox = BoundingBox(Vector3::Zero, Vector3::Zero);
|
||||
_localBox = BoundingBox(Vector3::Zero);
|
||||
_layoutOptions.Bounds = Rectangle(-100, -100, 200, 200);
|
||||
_layoutOptions.HorizontalAlignment = TextAlignment::Center;
|
||||
_layoutOptions.VerticalAlignment = TextAlignment::Center;
|
||||
@@ -92,7 +92,7 @@ void TextRender::UpdateLayout()
|
||||
_vb0.Clear();
|
||||
_vb1.Clear();
|
||||
_vb2.Clear();
|
||||
_localBox = BoundingBox(Vector3::Zero, Vector3::Zero);
|
||||
_localBox = BoundingBox(Vector3::Zero);
|
||||
BoundingBox::Transform(_localBox, _world, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
#if USE_PRECISE_MESH_INTERSECTS
|
||||
@@ -291,7 +291,7 @@ void TextRender::UpdateLayout()
|
||||
if (_ib.Data.IsEmpty())
|
||||
{
|
||||
// Empty
|
||||
box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
box = BoundingBox(_transform.Translation);
|
||||
}
|
||||
_localBox = box;
|
||||
BoundingBox::Transform(_localBox, _world, _box);
|
||||
|
||||
@@ -15,6 +15,7 @@ MMethod* UICanvas_PostDeserialize = nullptr;
|
||||
MMethod* UICanvas_OnEnable = nullptr;
|
||||
MMethod* UICanvas_OnDisable = nullptr;
|
||||
MMethod* UICanvas_EndPlay = nullptr;
|
||||
MMethod* UICanvas_ParentChanged = nullptr;
|
||||
|
||||
#define UICANVAS_INVOKE(event) \
|
||||
auto instance = GetManagedInstance(); \
|
||||
@@ -43,6 +44,7 @@ UICanvas::UICanvas(const SpawnParams& params)
|
||||
UICanvas_OnEnable = mclass->GetMethod("OnEnable");
|
||||
UICanvas_OnDisable = mclass->GetMethod("OnDisable");
|
||||
UICanvas_EndPlay = mclass->GetMethod("EndPlay");
|
||||
UICanvas_ParentChanged = mclass->GetMethod("ParentChanged");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +135,14 @@ void UICanvas::EndPlay()
|
||||
Actor::EndPlay();
|
||||
}
|
||||
|
||||
void UICanvas::OnParentChanged()
|
||||
{
|
||||
// Base
|
||||
Actor::OnParentChanged();
|
||||
|
||||
UICANVAS_INVOKE(ParentChanged);
|
||||
}
|
||||
|
||||
void UICanvas::OnEnable()
|
||||
{
|
||||
UICANVAS_INVOKE(OnEnable);
|
||||
@@ -154,6 +164,6 @@ void UICanvas::OnTransformChanged()
|
||||
// Base
|
||||
Actor::OnTransformChanged();
|
||||
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user