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

# Conflicts:
#	Source/Editor/Surface/SurfaceNode.cs
This commit is contained in:
Wojtek Figat
2021-01-27 10:39:44 +01:00
43 changed files with 1115 additions and 268 deletions

32
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,32 @@
# How to contribute to the FlaxEngine
For any questions, suggestions or help join our discord!
<a href="https://flaxengine.com/discord"><img src="https://discordapp.com/api/guilds/437989205315158016/widget.png"/></a>
Want to see whats planned for Flax?
Go check out our [Trello](https://trello.com/b/NQjLXRCP/flax-roadmap).
## **Found a bug?**
* Avoid opening any new issues without having checked if your problem has already been reported. If there are no currently open issues that fit your problem's description, feel free to [add it](https://github.com/FlaxEngine/FlaxEngine/issues/new).
* When writing an issue make sure to include a clear title and description as well as having filled out all the necessary information, depending on the severity of the issue also include the necessary log files and minidump.
* Try to following the given template when writing a new issue if possible.
## **Want to contribute?**
* When creating a PR for fixing an issue/bug make sure to describe as to what led to the fix for better understanding, for small and obvious fixes this is not really needed.
However make sure to mention the relevant issue where it was first reported if possible.
* For feature PR's the first thing you should evaluate is the value of your contribution, as in, what would it bring to this engine? Is it really required?
If its a small change you could preferably suggest it to us on our discord, else feel free to open up a PR for it.
* Ensure when creating a PR that your contribution is well explained with a adequate description and title.
* Generally, good code quality is expected, make sure your contribution works as intended and is appropriately commented where necessary.
Thank you for taking interest in contributing to Flax!

View File

@@ -23,7 +23,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// <inheritdoc /> /// <inheritdoc />
protected override void OnValueChanged() protected override void OnValueChanged()
{ {
var value = (StaticFlags)element.EnumComboBox.EnumTypeValue; var value = (StaticFlags)element.ComboBox.EnumTypeValue;
// If selected is single actor that has children, ask if apply flags to the sub objects as well // If selected is single actor that has children, ask if apply flags to the sub objects as well
if (Values.IsSingleObject && (StaticFlags)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren) if (Values.IsSingleObject && (StaticFlags)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren)

View File

@@ -40,7 +40,7 @@ namespace FlaxEditor.CustomEditors.Editors
{ {
var enumType = Values.Type.Type != typeof(object) || Values[0] == null ? TypeUtils.GetType(Values.Type) : Values[0].GetType(); var enumType = Values.Type.Type != typeof(object) || Values[0] == null ? TypeUtils.GetType(Values.Type) : Values[0].GetType();
element = layout.Enum(enumType, null, mode); element = layout.Enum(enumType, null, mode);
element.EnumComboBox.ValueChanged += OnValueChanged; element.ComboBox.ValueChanged += OnValueChanged;
} }
} }
@@ -49,7 +49,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// </summary> /// </summary>
protected virtual void OnValueChanged() protected virtual void OnValueChanged()
{ {
SetValue(element.EnumComboBox.EnumTypeValue); SetValue(element.ComboBox.EnumTypeValue);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -63,7 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors
} }
else else
{ {
element.EnumComboBox.EnumTypeValue = Values[0]; element.ComboBox.EnumTypeValue = Values[0];
} }
} }
} }

View File

@@ -16,7 +16,7 @@ namespace FlaxEditor.CustomEditors.Elements
/// <summary> /// <summary>
/// The combo box used to show enum values. /// The combo box used to show enum values.
/// </summary> /// </summary>
public EnumComboBox EnumComboBox; public EnumComboBox ComboBox;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="EnumElement"/> class. /// Initializes a new instance of the <see cref="EnumElement"/> class.
@@ -26,10 +26,10 @@ namespace FlaxEditor.CustomEditors.Elements
/// <param name="formatMode">The formatting mode.</param> /// <param name="formatMode">The formatting mode.</param>
public EnumElement(Type type, EnumComboBox.BuildEntriesDelegate customBuildEntriesDelegate = null, EnumDisplayAttribute.FormatMode formatMode = EnumDisplayAttribute.FormatMode.Default) public EnumElement(Type type, EnumComboBox.BuildEntriesDelegate customBuildEntriesDelegate = null, EnumDisplayAttribute.FormatMode formatMode = EnumDisplayAttribute.FormatMode.Default)
{ {
EnumComboBox = new EnumComboBox(type, customBuildEntriesDelegate, formatMode); ComboBox = new EnumComboBox(type, customBuildEntriesDelegate, formatMode);
} }
/// <inheritdoc /> /// <inheritdoc />
public override Control Control => EnumComboBox; public override Control Control => ComboBox;
} }
} }

View File

@@ -143,6 +143,8 @@ namespace FlaxEditor.GUI.Input
try try
{ {
var value = ShuntingYard.Parse(Text); var value = ShuntingYard.Parse(Text);
if (value < 0)
value = 0;
Value = (uint)value; Value = (uint)value;
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -45,5 +45,12 @@ namespace FlaxEditor.Options
[DefaultValue(60.0f), Limit(35.0f, 160.0f, 0.1f)] [DefaultValue(60.0f), Limit(35.0f, 160.0f, 0.1f)]
[EditorDisplay("Defaults", "Default Field Of View"), EditorOrder(140), Tooltip("The default field of view angle (in degrees) for the viewport camera.")] [EditorDisplay("Defaults", "Default Field Of View"), EditorOrder(140), Tooltip("The default field of view angle (in degrees) for the viewport camera.")]
public float DefaultFieldOfView { get; set; } = 60.0f; public float DefaultFieldOfView { get; set; } = 60.0f;
/// <summary>
/// Gets or sets if the panning direction is inverted for the viewport camera.
/// </summary>
[DefaultValue(false)]
[EditorDisplay("Defaults"), EditorOrder(150), Tooltip( "Invert the panning direction for the viewport camera." )]
public bool DefaultInvertPanning { get; set; } = false;
} }
} }

View File

@@ -791,7 +791,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Transform Node (local space)", Title = "Transform Node (local space)",
Description = "Transforms the skeleton node", Description = "Transforms the skeleton node",
Flags = NodeFlags.AnimGraph, Flags = NodeFlags.AnimGraph,
Size = new Vector2(270, 130), Size = new Vector2(280, 130),
DefaultValues = new object[] DefaultValues = new object[]
{ {
string.Empty, string.Empty,
@@ -816,7 +816,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Transform Node (model space)", Title = "Transform Node (model space)",
Description = "Transforms the skeleton node", Description = "Transforms the skeleton node",
Flags = NodeFlags.AnimGraph, Flags = NodeFlags.AnimGraph,
Size = new Vector2(270, 130), Size = new Vector2(280, 130),
DefaultValues = new object[] DefaultValues = new object[]
{ {
string.Empty, string.Empty,
@@ -872,7 +872,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Get Node Transform (model space)", Title = "Get Node Transform (model space)",
Description = "Samples the skeleton node transformation (in model space)", Description = "Samples the skeleton node transformation (in model space)",
Flags = NodeFlags.AnimGraph, Flags = NodeFlags.AnimGraph,
Size = new Vector2(250, 40), Size = new Vector2(324, 40),
DefaultValues = new object[] DefaultValues = new object[]
{ {
string.Empty, string.Empty,
@@ -880,9 +880,10 @@ namespace FlaxEditor.Surface.Archetypes
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0),
NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 160, 0),
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"),
NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0),
NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1),
} }
}, },
new NodeArchetype new NodeArchetype
@@ -903,7 +904,7 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 1), NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 1),
NodeElementArchetype.Factory.Input(1, "Target", true, typeof(Vector3), 2), NodeElementArchetype.Factory.Input(1, "Target", true, typeof(Vector3), 2),
NodeElementArchetype.Factory.Input(2, "Weight", true, typeof(float), 3, 1), NodeElementArchetype.Factory.Input(2, "Weight", true, typeof(float), 3, 1),
NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 3, 120, 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 3, 160, 0),
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 3, "Node:"), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 3, "Node:"),
} }
}, },
@@ -913,7 +914,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Get Node Transform (local space)", Title = "Get Node Transform (local space)",
Description = "Samples the skeleton node transformation (in local space)", Description = "Samples the skeleton node transformation (in local space)",
Flags = NodeFlags.AnimGraph, Flags = NodeFlags.AnimGraph,
Size = new Vector2(250, 40), Size = new Vector2(316, 40),
DefaultValues = new object[] DefaultValues = new object[]
{ {
string.Empty, string.Empty,
@@ -923,7 +924,8 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0),
NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0),
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"),
NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0),
NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1),
} }
}, },
new NodeArchetype new NodeArchetype

View File

@@ -379,7 +379,7 @@ namespace FlaxEditor.Surface.Archetypes
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Output(0, "Value", typeof(uint), 0), NodeElementArchetype.Factory.Output(0, "Value", typeof(uint), 0),
NodeElementArchetype.Factory.Integer(0, 0, 0) NodeElementArchetype.Factory.UnsignedInteger(0, 0, 0, -1, 0, int.MaxValue)
} }
}, },
}; };

View File

@@ -868,8 +868,15 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < signature.Params.Length; i++) for (int i = 0; i < signature.Params.Length; i++)
{ {
ref var param = ref signature.Params[i]; ref var param = ref signature.Params[i];
if (param.Type != memberParameters[i].Type || param.IsOut != memberParameters[i].IsOut) ref var paramMember = ref memberParameters[i];
if (param.Type != paramMember.Type || param.IsOut != paramMember.IsOut)
{ {
// Special case: param.Type is serialized as just a type while paramMember.Type might be a reference for output parameters (eg. `out Int32` vs `out Int32&`)
var paramMemberTypeName = paramMember.Type.TypeName;
if (param.IsOut && param.IsOut == paramMember.IsOut && paramMember.Type.IsReference && !param.Type.IsReference &&
paramMemberTypeName.Substring(0, paramMemberTypeName.Length - 1) == param.Type.TypeName)
continue;
isInvalid = true; isInvalid = true;
break; break;
} }

View File

@@ -642,6 +642,85 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(0, "XYZ", typeof(Vector3), 0), NodeElementArchetype.Factory.Output(0, "XYZ", typeof(Vector3), 0),
} }
}, },
new NodeArchetype
{
TypeID = 26,
Title = "Blend Normals",
Description = "Blend two normal maps to create a single normal map",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(170, 40),
ConnectionsHints = ConnectionsHint.Vector,
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Base Normal", true, typeof(Vector3), 0),
NodeElementArchetype.Factory.Input(1, "Additional Normal", true, typeof(Vector3), 1),
NodeElementArchetype.Factory.Output(0, "Result", typeof(Vector3), 2)
}
},
new NodeArchetype
{
TypeID = 27,
Title = "Rotator",
Description = "Rotates UV coordinates according to a scalar angle (0-1)",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(150, 55),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Vector2), 0),
NodeElementArchetype.Factory.Input(1, "Center", true, typeof(Vector2), 1),
NodeElementArchetype.Factory.Input(2, "Rotation Angle", true, typeof(float), 2),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3),
}
},
new NodeArchetype
{
TypeID = 28,
Title = "Sphere Mask",
Description = "Creates a sphere mask",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(150, 100),
ConnectionsHints = ConnectionsHint.Vector,
IndependentBoxes = new[]
{
0,
1
},
DefaultValues = new object[]
{
0.3f,
0.5f,
false
},
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "A", true, null, 0),
NodeElementArchetype.Factory.Input(1, "B", true, null, 1),
NodeElementArchetype.Factory.Input(2, "Radius", true, typeof(float), 2, 0),
NodeElementArchetype.Factory.Input(3, "Hardness", true, typeof(float), 3, 1),
NodeElementArchetype.Factory.Input(4, "Invert", true, typeof(bool), 4, 2),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5),
}
},
new NodeArchetype
{
TypeID = 29,
Title = "UV Tiling & Offset",
Description = "Takes UVs and applies tiling and offset",
Flags = NodeFlags.MaterialGraph,
Size = new Vector2(175, 60),
DefaultValues = new object[]
{
Vector2.One,
Vector2.Zero
},
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Vector2), 0),
NodeElementArchetype.Factory.Input(1, "Tiling", true, typeof(Vector2), 1, 0),
NodeElementArchetype.Factory.Input(2, "Offset", true, typeof(Vector2), 2, 1),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3),
}
}
}; };
} }
} }

View File

@@ -404,6 +404,29 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(0, "A | B", null, 2), NodeElementArchetype.Factory.Output(0, "A | B", null, 2),
} }
}, },
new NodeArchetype
{
TypeID = 48,
Title = "Remap",
Description = "Remaps a value from one range to another, so for example having 25 in a range of 0 to 100 being remapped to 0 to 1 would return 0.25",
Flags = NodeFlags.AllGraphs,
Size = new Vector2(175, 75),
DefaultValues = new object[]
{
25.0f,
new Vector2(0.0f, 100.0f),
new Vector2(0.0f, 1.0f),
false
},
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Value", true, typeof(float), 0, 0),
NodeElementArchetype.Factory.Input(1, "In Range", true, typeof(Vector2), 1, 1),
NodeElementArchetype.Factory.Input(2, "Out Range", true, typeof(Vector2), 2, 2),
NodeElementArchetype.Factory.Input(3, "Clamp", true, typeof(bool), 3, 3),
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 4),
}
},
}; };
} }
} }

View File

@@ -486,7 +486,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack X component from Vector", Description = "Unpack X component from Vector",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector, ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30), Size = new Vector2(110, 30),
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -500,7 +500,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack Y component from Vector", Description = "Unpack Y component from Vector",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector, ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30), Size = new Vector2(110, 30),
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -514,7 +514,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack Z component from Vector", Description = "Unpack Z component from Vector",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector, ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30), Size = new Vector2(110, 30),
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -528,7 +528,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack W component from Vector", Description = "Unpack W component from Vector",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector, ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30), Size = new Vector2(110, 30),
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -544,7 +544,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack XY components from Vector", Description = "Unpack XY components from Vector",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector, ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30), Size = new Vector2(110, 30),
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -558,7 +558,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack XZ components from Vector", Description = "Unpack XZ components from Vector",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector, ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30), Size = new Vector2(110, 30),
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -572,7 +572,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack YZ components from Vector", Description = "Unpack YZ components from Vector",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector, ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30), Size = new Vector2(110, 30),
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
@@ -588,7 +588,7 @@ namespace FlaxEditor.Surface.Archetypes
Description = "Unpack XYZ components from Vector", Description = "Unpack XYZ components from Vector",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
ConnectionsHints = ConnectionsHint.Vector, ConnectionsHints = ConnectionsHint.Vector,
Size = new Vector2(160, 30), Size = new Vector2(110, 30),
Elements = new[] Elements = new[]
{ {
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),

View File

@@ -97,7 +97,7 @@ namespace FlaxEditor.Surface.Elements
var connections = Connections.ToArray(); var connections = Connections.ToArray();
for (int i = 0; i < connections.Length; i++) for (int i = 0; i < connections.Length; i++)
{ {
var targetBox = Connections[i]; var targetBox = connections[i];
// Break connection // Break connection
Connections.Remove(targetBox); Connections.Remove(targetBox);
@@ -565,7 +565,28 @@ namespace FlaxEditor.Surface.Elements
{ {
_isMouseDown = false; _isMouseDown = false;
if (Surface.CanEdit) if (Surface.CanEdit)
Surface.ConnectingStart(this); {
if (!IsOutput && HasSingleConnection)
{
var connectedBox = Connections[0];
if (Surface.Undo != null)
{
var action = new ConnectBoxesAction((InputBox)this, (OutputBox)connectedBox, false);
BreakConnection(connectedBox);
action.End();
Surface.Undo.AddAction(action);
}
else
{
BreakConnection(connectedBox);
}
Surface.ConnectingStart(connectedBox);
}
else
{
Surface.ConnectingStart(this);
}
}
} }
base.OnMouseLeave(); base.OnMouseLeave();
} }

View File

@@ -85,7 +85,7 @@ namespace FlaxEditor.Surface.Elements
else if (value is Vector4 valueVec4) else if (value is Vector4 valueVec4)
result = (uint)(arch.BoxID == 0 ? valueVec4.X : arch.BoxID == 1 ? valueVec4.Y : arch.BoxID == 2 ? valueVec4.Z : valueVec4.W); result = (uint)(arch.BoxID == 0 ? valueVec4.X : arch.BoxID == 1 ? valueVec4.Y : arch.BoxID == 2 ? valueVec4.Z : valueVec4.W);
else else
result = 0; result = 0u;
return result; return result;
} }

View File

@@ -239,6 +239,32 @@ namespace FlaxEditor.Surface
}; };
} }
/// <summary>
/// Creates new Unsigned Integer value element description.
/// </summary>
/// <param name="x">The x location (in node area space).</param>
/// <param name="y">The y location (in node area space).</param>
/// <param name="valueIndex">The index of the node variable linked as the input. Useful to make a physical connection between input box and default value for it.</param>
/// <param name="component">The index of the component to edit. For vectors this can be set to modify only single component of it. Eg. for vec2 value component set to 1 will edit only Y component. Default value -1 will be used to edit whole value.</param>
/// <param name="valueMin">The minimum value range.</param>
/// <param name="valueMax">The maximum value range.</param>
/// <returns>The archetype.</returns>
public static NodeElementArchetype UnsignedInteger(float x, float y, int valueIndex = -1, int component = -1, uint valueMin = 0, uint valueMax = 1000000)
{
return new NodeElementArchetype
{
Type = NodeElementType.UnsignedIntegerValue,
Position = new Vector2(Constants.NodeMarginX + x, Constants.NodeMarginY + Constants.NodeHeaderSize + y),
Text = null,
Single = false,
ValueIndex = valueIndex,
ValueMin = valueMin,
ValueMax = valueMax,
BoxID = -1,
ConnectionsType = ScriptType.Null
};
}
/// <summary> /// <summary>
/// Creates new Float value element description. /// Creates new Float value element description.
/// </summary> /// </summary>

View File

@@ -94,5 +94,10 @@ namespace FlaxEditor.Surface
/// The actor picker. /// The actor picker.
/// </summary> /// </summary>
Actor = 19, Actor = 19,
/// <summary>
/// The unsigned integer value.
/// </summary>
UnsignedIntegerValue = 20,
} }
} }

View File

@@ -278,6 +278,9 @@ namespace FlaxEditor.Surface
case NodeElementType.Actor: case NodeElementType.Actor:
element = new ActorSelect(this, arch); element = new ActorSelect(this, arch);
break; break;
case NodeElementType.UnsignedIntegerValue:
element = new UnsignedIntegerValue(this, arch);
break;
//default: throw new NotImplementedException("Unknown node element type: " + arch.Type); //default: throw new NotImplementedException("Unknown node element type: " + arch.Type);
} }
if (element != null) if (element != null)
@@ -314,12 +317,16 @@ namespace FlaxEditor.Surface
{ {
if (type == ScriptType.Null) if (type == ScriptType.Null)
type = new ScriptType(typeof(object)); type = new ScriptType(typeof(object));
// Try to reuse box
var box = GetBox(id); var box = GetBox(id);
if ((isOut && box is InputBox) || (!isOut && box is OutputBox)) if ((isOut && box is InputBox) || (!isOut && box is OutputBox))
{ {
box.Dispose(); box.Dispose();
box = null; box = null;
} }
// Create new if missing
if (box == null) if (box == null)
{ {
if (isOut) if (isOut)
@@ -330,10 +337,15 @@ namespace FlaxEditor.Surface
} }
else else
{ {
// Sync properties for exiting box
box.Text = text; box.Text = text;
box.CurrentType = type; box.CurrentType = type;
box.Y = Constants.NodeMarginY + Constants.NodeHeaderSize + yLevel * Constants.LayoutOffsetY; box.Y = Constants.NodeMarginY + Constants.NodeHeaderSize + yLevel * Constants.LayoutOffsetY;
} }
// Update box
box.OnConnectionsChanged();
return box; return box;
} }

View File

@@ -15,6 +15,7 @@ namespace FlaxEditor.Surface
{ {
private ContextMenuButton _cmCopyButton; private ContextMenuButton _cmCopyButton;
private ContextMenuButton _cmDuplicateButton; private ContextMenuButton _cmDuplicateButton;
private ContextMenuButton _cmFormatNodesConnectionButton;
private ContextMenuButton _cmRemoveNodeConnectionsButton; private ContextMenuButton _cmRemoveNodeConnectionsButton;
private ContextMenuButton _cmRemoveBoxConnectionsButton; private ContextMenuButton _cmRemoveBoxConnectionsButton;
private readonly Vector2 ContextMenuOffset = new Vector2(5); private readonly Vector2 ContextMenuOffset = new Vector2(5);
@@ -216,6 +217,13 @@ namespace FlaxEditor.Surface
} }
}).Enabled = Nodes.Any(x => x.Breakpoint.Set && x.Breakpoint.Enabled); }).Enabled = Nodes.Any(x => x.Breakpoint.Set && x.Breakpoint.Enabled);
} }
menu.AddSeparator();
_cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () =>
{
FormatGraph(SelectedNodes);
});
_cmFormatNodesConnectionButton.Enabled = HasNodesSelection;
menu.AddSeparator(); menu.AddSeparator();
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () => _cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () =>

View File

@@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FlaxEngine;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Surface.Undo;
namespace FlaxEditor.Surface
{
public partial class VisjectSurface
{
// Reference https://github.com/stefnotch/xnode-graph-formatter/blob/812e08e71c7b9b7eb0810dbdfb0a9a1034da6941/Assets/Examples/MathGraph/Editor/MathGraphEditor.cs
private class NodeFormattingData
{
/// <summary>
/// Starting from 0 at the main nodes
/// </summary>
public int Layer;
/// <summary>
/// Position in the layer
/// </summary>
public int Offset;
/// <summary>
/// How far the subtree needs to be moved additionally
/// </summary>
public int SubtreeOffset;
}
/// <summary>
/// Formats a graph where the nodes can be disjointed.
/// Uses the Sugiyama method
/// </summary>
/// <param name="nodes">List of nodes</param>
protected void FormatGraph(List<SurfaceNode> nodes)
{
if (nodes.Count <= 1 || !CanEdit) return;
var nodesToVisit = new HashSet<SurfaceNode>(nodes);
// While we haven't formatted every node
while (nodesToVisit.Count > 0)
{
// Run a search in both directions
var connectedNodes = new List<SurfaceNode>();
var queue = new Queue<SurfaceNode>();
var startNode = nodesToVisit.First();
nodesToVisit.Remove(startNode);
queue.Enqueue(startNode);
while (queue.Count > 0)
{
var node = queue.Dequeue();
connectedNodes.Add(node);
for (int i = 0; i < node.Elements.Count; i++)
{
if (node.Elements[i] is Box box)
{
for (int j = 0; j < box.Connections.Count; j++)
{
if (nodesToVisit.Contains(box.Connections[j].ParentNode))
{
nodesToVisit.Remove(box.Connections[j].ParentNode);
queue.Enqueue(box.Connections[j].ParentNode);
}
}
}
}
}
FormatConnectedGraph(connectedNodes);
}
}
/// <summary>
/// Formats a graph where all nodes are connected
/// </summary>
/// <param name="nodes">List of connected nodes</param>
protected void FormatConnectedGraph(List<SurfaceNode> nodes)
{
if (nodes.Count <= 1 || !CanEdit) return;
var boundingBox = GetNodesBounds(nodes);
var nodeData = nodes.ToDictionary(n => n, n => new NodeFormattingData { });
// Rightmost nodes with none of our nodes to the right of them
var endNodes = nodes
.Where(n => !n.GetBoxes().Any(b => b.IsOutput && b.Connections.Any(c => nodeData.ContainsKey(c.ParentNode))))
.OrderBy(n => n.Top) // Keep their relative order
.ToList();
// Longest path layering
int maxLayer = SetLayers(nodeData, endNodes);
// Set the vertical offsets
int maxOffset = SetOffsets(nodeData, endNodes, maxLayer);
// Layout the nodes
// Get the largest nodes in the Y and X direction
float[] widths = new float[maxLayer + 1];
float[] heights = new float[maxOffset + 1];
for (int i = 0; i < nodes.Count; i++)
{
if (nodeData.TryGetValue(nodes[i], out var data))
{
if (nodes[i].Width > widths[data.Layer])
{
widths[data.Layer] = nodes[i].Width;
}
if (nodes[i].Height > heights[data.Offset])
{
heights[data.Offset] = nodes[i].Height;
}
}
}
Vector2 minDistanceBetweenNodes = new Vector2(30, 30);
// Figure out the node positions (aligned to a grid)
float[] nodeXPositions = new float[widths.Length];
for (int i = 1; i < widths.Length; i++)
{
// Go from right to left (backwards) through the nodes
nodeXPositions[i] = nodeXPositions[i - 1] + minDistanceBetweenNodes.X + widths[i];
}
float[] nodeYPositions = new float[heights.Length];
for (int i = 1; i < heights.Length; i++)
{
// Go from top to bottom through the nodes
nodeYPositions[i] = nodeYPositions[i - 1] + heights[i - 1] + minDistanceBetweenNodes.Y;
}
// Set the node positions
var undoActions = new List<MoveNodesAction>();
var topRightPosition = endNodes[0].Location;
for (int i = 0; i < nodes.Count; i++)
{
if (nodeData.TryGetValue(nodes[i], out var data))
{
Vector2 newLocation = new Vector2(-nodeXPositions[data.Layer], nodeYPositions[data.Offset]) + topRightPosition;
Vector2 locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
if (Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
}
MarkAsEdited(false);
Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes"));
}
/// <summary>
/// Assigns a layer to every node
/// </summary>
/// <param name="nodeData">The exta node data</param>
/// <param name="endNodes">The end nodes</param>
/// <returns>The number of the maximum layer</returns>
private int SetLayers(Dictionary<SurfaceNode, NodeFormattingData> nodeData, List<SurfaceNode> endNodes)
{
// Longest path layering
int maxLayer = 0;
var stack = new Stack<SurfaceNode>(endNodes);
while (stack.Count > 0)
{
var node = stack.Pop();
int layer = nodeData[node].Layer;
for (int i = 0; i < node.Elements.Count; i++)
{
if (node.Elements[i] is InputBox box && box.HasAnyConnection)
{
var childNode = box.Connections[0].ParentNode;
if (nodeData.TryGetValue(childNode, out var data))
{
int nodeLayer = Math.Max(data.Layer, layer + 1);
data.Layer = nodeLayer;
if (nodeLayer > maxLayer)
{
maxLayer = nodeLayer;
}
stack.Push(childNode);
}
}
}
}
return maxLayer;
}
/// <summary>
/// Sets the node offsets
/// </summary>
/// <param name="nodeData">The exta node data</param>
/// <param name="endNodes">The end nodes</param>
/// <param name="maxLayer">The number of the maximum layer</param>
/// <returns>The number of the maximum offset</returns>
private int SetOffsets(Dictionary<SurfaceNode, NodeFormattingData> nodeData, List<SurfaceNode> endNodes, int maxLayer)
{
int maxOffset = 0;
// Keeps track of the largest offset (Y axis) for every layer
int[] offsets = new int[maxLayer + 1];
var visitedNodes = new HashSet<SurfaceNode>();
void SetOffsets(SurfaceNode node, NodeFormattingData straightParentData)
{
if (!nodeData.TryGetValue(node, out var data)) return;
// If we realize that the current node would collide with an already existing node in this layer
if (data.Layer >= 0 && offsets[data.Layer] > data.Offset)
{
// Move the entire sub-tree down
straightParentData.SubtreeOffset = Math.Max(straightParentData.SubtreeOffset, offsets[data.Layer] - data.Offset);
}
// Keeps track of the offset of the last direct child we visited
int childOffset = data.Offset;
bool straightChild = true;
// Run the algorithm for every child
for (int i = 0; i < node.Elements.Count; i++)
{
if (node.Elements[i] is InputBox box && box.HasAnyConnection)
{
var childNode = box.Connections[0].ParentNode;
if (!visitedNodes.Contains(childNode) && nodeData.TryGetValue(childNode, out var childData))
{
visitedNodes.Add(childNode);
childData.Offset = childOffset;
SetOffsets(childNode, straightChild ? straightParentData : childData);
childOffset = childData.Offset + 1;
straightChild = false;
}
}
}
if (data.Layer >= 0)
{
// When coming out of the recursion, apply the extra subtree offsets
data.Offset += straightParentData.SubtreeOffset;
if (data.Offset > maxOffset)
{
maxOffset = data.Offset;
}
offsets[data.Layer] = data.Offset + 1;
}
}
{
// An imaginary final node
var endNodeData = new NodeFormattingData { Layer = -1 };
int childOffset = 0;
bool straightChild = true;
for (int i = 0; i < endNodes.Count; i++)
{
if (nodeData.TryGetValue(endNodes[i], out var childData))
{
visitedNodes.Add(endNodes[i]);
childData.Offset = childOffset;
SetOffsets(endNodes[i], straightChild ? endNodeData : childData);
childOffset = childData.Offset + 1;
straightChild = false;
}
}
}
return maxOffset;
}
}
}

View File

@@ -24,7 +24,9 @@ namespace FlaxEditor.Surface
private class InputBracket private class InputBracket
{ {
private readonly float DefaultWidth = 120f;
private readonly Margin _padding = new Margin(10f); private readonly Margin _padding = new Margin(10f);
public Box Box { get; } public Box Box { get; }
public Vector2 EndBracketPosition { get; } public Vector2 EndBracketPosition { get; }
public List<SurfaceNode> Nodes { get; } = new List<SurfaceNode>(); public List<SurfaceNode> Nodes { get; } = new List<SurfaceNode>();
@@ -33,7 +35,7 @@ namespace FlaxEditor.Surface
public InputBracket(Box box, Vector2 nodePosition) public InputBracket(Box box, Vector2 nodePosition)
{ {
Box = box; Box = box;
EndBracketPosition = nodePosition; EndBracketPosition = nodePosition + new Vector2(DefaultWidth, 0);
Update(); Update();
} }
@@ -47,11 +49,10 @@ namespace FlaxEditor.Surface
} }
else else
{ {
area = new Rectangle(EndBracketPosition, new Vector2(120f, 80f)); area = new Rectangle(EndBracketPosition, new Vector2(DefaultWidth, 80f));
} }
_padding.ExpandRectangle(ref area); _padding.ExpandRectangle(ref area);
Vector2 endPoint = area.Location + new Vector2(area.Width, area.Height / 2f); Vector2 offset = EndBracketPosition - area.UpperRight;
Vector2 offset = EndBracketPosition - endPoint;
area.Location += offset; area.Location += offset;
Area = area; Area = area;
if (!offset.IsZero) if (!offset.IsZero)
@@ -99,18 +100,6 @@ namespace FlaxEditor.Surface
/// </summary> /// </summary>
public event Window.MouseWheelDelegate CustomMouseWheel; public event Window.MouseWheelDelegate CustomMouseWheel;
/// <summary>
/// Gets the node under the mouse location.
/// </summary>
/// <returns>The node or null if no intersection.</returns>
public SurfaceNode GetNodeUnderMouse()
{
var pos = _rootControl.PointFromParent(ref _mousePos);
if (_rootControl.GetChildAt(pos) is SurfaceNode node)
return node;
return null;
}
/// <summary> /// <summary>
/// Gets the control under the mouse location. /// Gets the control under the mouse location.
/// </summary> /// </summary>
@@ -118,9 +107,7 @@ namespace FlaxEditor.Surface
public SurfaceControl GetControlUnderMouse() public SurfaceControl GetControlUnderMouse()
{ {
var pos = _rootControl.PointFromParent(ref _mousePos); var pos = _rootControl.PointFromParent(ref _mousePos);
if (_rootControl.GetChildAtRecursive(pos) is SurfaceControl control) return _rootControl.GetChildAt(pos) as SurfaceControl;
return control;
return null;
} }
private void UpdateSelectionRectangle() private void UpdateSelectionRectangle()
@@ -464,15 +451,7 @@ namespace FlaxEditor.Surface
{ {
// Check if any control is under the mouse // Check if any control is under the mouse
_cmStartPos = location; _cmStartPos = location;
if (controlUnderMouse != null) if (controlUnderMouse == null)
{
if (!HasNodesSelection)
Select(controlUnderMouse);
// Show secondary context menu
ShowSecondaryCM(_cmStartPos, controlUnderMouse);
}
else
{ {
// Show primary context menu // Show primary context menu
ShowPrimaryMenu(_cmStartPos); ShowPrimaryMenu(_cmStartPos);
@@ -708,31 +687,38 @@ namespace FlaxEditor.Surface
private Vector2 FindEmptySpace(Box box) private Vector2 FindEmptySpace(Box box)
{ {
int boxIndex = 0; Vector2 distanceBetweenNodes = new Vector2(30, 30);
var node = box.ParentNode; var node = box.ParentNode;
// Same height as node
float yLocation = node.Top;
for (int i = 0; i < node.Elements.Count; i++) for (int i = 0; i < node.Elements.Count; i++)
{ {
// Box on the same side above the current box
if (node.Elements[i] is Box nodeBox && nodeBox.IsOutput == box.IsOutput && nodeBox.Y < box.Y) if (node.Elements[i] is Box nodeBox && nodeBox.IsOutput == box.IsOutput && nodeBox.Y < box.Y)
{ {
boxIndex++; // Below connected node
yLocation = Mathf.Max(yLocation, nodeBox.ParentNode.Bottom + distanceBetweenNodes.Y);
} }
} }
// TODO: Dodge the other nodes // TODO: Dodge the other nodes
Vector2 distanceBetweenNodes = new Vector2(40, 20); float xLocation = node.Location.X;
const float NodeHeight = 120; if (box.IsOutput)
{
xLocation += node.Width + distanceBetweenNodes.X;
}
else
{
xLocation += -120 - distanceBetweenNodes.X;
}
float direction = box.IsOutput ? 1 : -1; return new Vector2(
xLocation,
Vector2 newNodeLocation = node.Location + yLocation
new Vector2( );
(node.Width + distanceBetweenNodes.X) * direction,
boxIndex * (NodeHeight + distanceBetweenNodes.Y)
);
return newNodeLocation;
} }
} }
} }

View File

@@ -188,8 +188,16 @@ namespace FlaxEditor.Viewport.Cameras
if (input.IsPanning) if (input.IsPanning)
{ {
var panningSpeed = 0.8f; var panningSpeed = 0.8f;
position -= right * (mouseDelta.X * panningSpeed); if (Viewport.InvertPanning)
position -= up * (mouseDelta.Y * panningSpeed); {
position += up * (mouseDelta.Y * panningSpeed);
position += right * (mouseDelta.X * panningSpeed);
}
else
{
position -= right * (mouseDelta.X * panningSpeed);
position -= up * (mouseDelta.Y * panningSpeed);
}
} }
// Move // Move

View File

@@ -180,6 +180,7 @@ namespace FlaxEditor.Viewport
private float _orthoSize = 1.0f; private float _orthoSize = 1.0f;
private bool _isOrtho = false; private bool _isOrtho = false;
private float _wheelMovementChangeDeltaSum = 0; private float _wheelMovementChangeDeltaSum = 0;
private bool _invertPanning;
/// <summary> /// <summary>
/// Speed of the mouse. /// Speed of the mouse.
@@ -403,6 +404,15 @@ namespace FlaxEditor.Viewport
set => _isOrtho = value; set => _isOrtho = value;
} }
/// <summary>
/// Gets or sets if the panning direction is inverted.
/// </summary>
public bool InvertPanning
{
get => _invertPanning;
set => _invertPanning = value;
}
/// <summary> /// <summary>
/// The input actions collection to processed during user input. /// The input actions collection to processed during user input.
/// </summary> /// </summary>
@@ -434,6 +444,7 @@ namespace FlaxEditor.Viewport
_nearPlane = options.Viewport.DefaultNearPlane; _nearPlane = options.Viewport.DefaultNearPlane;
_farPlane = options.Viewport.DefaultFarPlane; _farPlane = options.Viewport.DefaultFarPlane;
_fieldOfView = options.Viewport.DefaultFieldOfView; _fieldOfView = options.Viewport.DefaultFieldOfView;
_invertPanning = options.Viewport.DefaultInvertPanning;
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
OnEditorOptionsChanged(options); OnEditorOptionsChanged(options);
@@ -454,7 +465,7 @@ namespace FlaxEditor.Viewport
var button = camSpeedCM.AddButton(v.ToString()); var button = camSpeedCM.AddButton(v.ToString());
button.Tag = v; button.Tag = v;
} }
camSpeedCM.ButtonClicked += (button) => MovementSpeed = (float)button.Tag; camSpeedCM.ButtonClicked += button => MovementSpeed = (float)button.Tag;
camSpeedCM.VisibleChanged += WidgetCamSpeedShowHide; camSpeedCM.VisibleChanged += WidgetCamSpeedShowHide;
camSpeedButton.Parent = camSpeed; camSpeedButton.Parent = camSpeed;
camSpeed.Parent = this; camSpeed.Parent = this;
@@ -515,9 +526,9 @@ namespace FlaxEditor.Viewport
// Orthographic // Orthographic
{ {
var ortho = ViewWidgetButtonMenu.AddButton("Orthographic"); var ortho = ViewWidgetButtonMenu.AddButton("Orthographic");
var orthoValue = new CheckBox(75, 2, _isOrtho); var orthoValue = new CheckBox(90, 2, _isOrtho);
orthoValue.Parent = ortho; orthoValue.Parent = ortho;
orthoValue.StateChanged += (checkBox) => orthoValue.StateChanged += checkBox =>
{ {
if (checkBox.Checked != _isOrtho) if (checkBox.Checked != _isOrtho)
{ {
@@ -543,10 +554,10 @@ namespace FlaxEditor.Viewport
// Field of View // Field of View
{ {
var fov = ViewWidgetButtonMenu.AddButton("Field Of View"); var fov = ViewWidgetButtonMenu.AddButton("Field Of View");
var fovValue = new FloatValueBox(1, 75, 2, 50.0f, 35.0f, 160.0f, 0.1f); var fovValue = new FloatValueBox(1, 90, 2, 70.0f, 35.0f, 160.0f, 0.1f);
fovValue.Parent = fov; fovValue.Parent = fov;
fovValue.ValueChanged += () => _fieldOfView = fovValue.Value; fovValue.ValueChanged += () => _fieldOfView = fovValue.Value;
ViewWidgetButtonMenu.VisibleChanged += (control) => ViewWidgetButtonMenu.VisibleChanged += control =>
{ {
fov.Visible = !_isOrtho; fov.Visible = !_isOrtho;
fovValue.Value = _fieldOfView; fovValue.Value = _fieldOfView;
@@ -556,10 +567,10 @@ namespace FlaxEditor.Viewport
// Ortho Scale // Ortho Scale
{ {
var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale"); var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale");
var orthoSizeValue = new FloatValueBox(_orthoSize, 75, 2, 50.0f, 0.001f, 100000.0f, 0.01f); var orthoSizeValue = new FloatValueBox(_orthoSize, 90, 2, 70.0f, 0.001f, 100000.0f, 0.01f);
orthoSizeValue.Parent = orthoSize; orthoSizeValue.Parent = orthoSize;
orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value; orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value;
ViewWidgetButtonMenu.VisibleChanged += (control) => ViewWidgetButtonMenu.VisibleChanged += control =>
{ {
orthoSize.Visible = _isOrtho; orthoSize.Visible = _isOrtho;
orthoSizeValue.Value = _orthoSize; orthoSizeValue.Value = _orthoSize;
@@ -569,7 +580,7 @@ namespace FlaxEditor.Viewport
// Near Plane // Near Plane
{ {
var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane"); var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane");
var nearPlaneValue = new FloatValueBox(2.0f, 75, 2, 50.0f, 0.001f, 1000.0f); var nearPlaneValue = new FloatValueBox(2.0f, 90, 2, 70.0f, 0.001f, 1000.0f);
nearPlaneValue.Parent = nearPlane; nearPlaneValue.Parent = nearPlane;
nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value; nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane; ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane;
@@ -578,7 +589,7 @@ namespace FlaxEditor.Viewport
// Far Plane // Far Plane
{ {
var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane"); var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane");
var farPlaneValue = new FloatValueBox(1000, 75, 2, 50.0f, 10.0f); var farPlaneValue = new FloatValueBox(1000, 90, 2, 70.0f, 10.0f);
farPlaneValue.Parent = farPlane; farPlaneValue.Parent = farPlane;
farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value; farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane; ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane;
@@ -587,7 +598,7 @@ namespace FlaxEditor.Viewport
// Brightness // Brightness
{ {
var brightness = ViewWidgetButtonMenu.AddButton("Brightness"); var brightness = ViewWidgetButtonMenu.AddButton("Brightness");
var brightnessValue = new FloatValueBox(1.0f, 75, 2, 50.0f, 0.001f, 10.0f, 0.001f); var brightnessValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.001f, 10.0f, 0.001f);
brightnessValue.Parent = brightness; brightnessValue.Parent = brightness;
brightnessValue.ValueChanged += () => Brightness = brightnessValue.Value; brightnessValue.ValueChanged += () => Brightness = brightnessValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => brightnessValue.Value = Brightness; ViewWidgetButtonMenu.VisibleChanged += control => brightnessValue.Value = Brightness;
@@ -596,11 +607,26 @@ namespace FlaxEditor.Viewport
// Resolution // Resolution
{ {
var resolution = ViewWidgetButtonMenu.AddButton("Resolution"); var resolution = ViewWidgetButtonMenu.AddButton("Resolution");
var resolutionValue = new FloatValueBox(1.0f, 75, 2, 50.0f, 0.1f, 4.0f, 0.001f); var resolutionValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.1f, 4.0f, 0.001f);
resolutionValue.Parent = resolution; resolutionValue.Parent = resolution;
resolutionValue.ValueChanged += () => ResolutionScale = resolutionValue.Value; resolutionValue.ValueChanged += () => ResolutionScale = resolutionValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale; ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale;
} }
// Invert Panning
{
var invert = ViewWidgetButtonMenu.AddButton("Invert Panning");
var invertValue = new CheckBox(90, 2, _invertPanning);
invertValue.Parent = invert;
invertValue.StateChanged += checkBox =>
{
if (checkBox.Checked != _invertPanning)
{
_invertPanning = checkBox.Checked;
}
};
ViewWidgetButtonMenu.VisibleChanged += control => invertValue.Checked = _invertPanning;
}
} }
// Link for task event // Link for task event

View File

@@ -84,7 +84,7 @@ namespace FlaxEditor.Viewport.Previews
// Preview LOD // Preview LOD
{ {
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f); var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f);
previewLODValue.Parent = previewLOD; previewLODValue.Parent = previewLOD;
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value; previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD; ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;

View File

@@ -53,7 +53,7 @@ namespace FlaxEditor.Viewport.Previews
// Preview LOD // Preview LOD
{ {
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f); var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f);
previewLODValue.Parent = previewLOD; previewLODValue.Parent = previewLOD;
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value; previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD; ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;

View File

@@ -67,7 +67,7 @@ namespace FlaxEditor.Viewport.Previews
if (useWidgets) if (useWidgets)
{ {
var playbackDuration = ViewWidgetButtonMenu.AddButton("Duration"); var playbackDuration = ViewWidgetButtonMenu.AddButton("Duration");
var playbackDurationValue = new FloatValueBox(_playbackDuration, 75, 2, 50.0f, 0.1f, 1000000.0f, 0.1f); var playbackDurationValue = new FloatValueBox(_playbackDuration, 90, 2, 70.0f, 0.1f, 1000000.0f, 0.1f);
playbackDurationValue.Parent = playbackDuration; playbackDurationValue.Parent = playbackDuration;
playbackDurationValue.ValueChanged += () => PlaybackDuration = playbackDurationValue.Value; playbackDurationValue.ValueChanged += () => PlaybackDuration = playbackDurationValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => playbackDurationValue.Value = PlaybackDuration; ViewWidgetButtonMenu.VisibleChanged += control => playbackDurationValue.Value = PlaybackDuration;

View File

@@ -40,6 +40,12 @@ namespace FlaxEditor.Windows.Assets
_presenter.Modified += MarkAsEdited; _presenter.Modified += MarkAsEdited;
} }
private void OnScriptsReloadBegin()
{
Close();
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
}
/// <inheritdoc /> /// <inheritdoc />
public override void Save() public override void Save()
{ {
@@ -76,6 +82,10 @@ namespace FlaxEditor.Windows.Assets
_presenter.Select(_object); _presenter.Select(_object);
ClearEditedFlag(); ClearEditedFlag();
// Auto-close on scripting reload if json asset is from game scripts (it might be reloaded)
if (_object != null && FlaxEngine.Scripting.IsTypeFromGameScripts(_object.GetType()))
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
base.OnAssetLoaded(); base.OnAssetLoaded();
} }

View File

@@ -224,7 +224,7 @@ namespace FlaxEditor.Windows
/// </summary> /// </summary>
private void ExportSelection() private void ExportSelection()
{ {
if(FileSystem.ShowBrowseFolderDialog(Editor.Windows.MainWindow, null, "Select the output folder", out var outputFolder)) if (FileSystem.ShowBrowseFolderDialog(Editor.Windows.MainWindow, null, "Select the output folder", out var outputFolder))
return; return;
var selection = _view.Selection; var selection = _view.Selection;

View File

@@ -15,7 +15,7 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2
Vector2 n = p1 - p0; Vector2 n = p1 - p0;
const float length = n.Length(); const float length = n.Length();
if (length < 1e-10) if (length < 1e-10f)
{ {
// Both points are the same, just give any // Both points are the same, just give any
result = p0; result = p0;
@@ -24,7 +24,7 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2
n /= length; n /= length;
const float dot = Vector2::Dot(n, p); const float dot = Vector2::Dot(n, p);
if (dot <= 0.0) if (dot <= 0.0f)
{ {
// Before first point // Before first point
result = p0; result = p0;
@@ -41,6 +41,45 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2
} }
} }
Vector2 CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1)
{
Vector2 result;
ClosestPointPointLine(point, p0, p1, result);
return result;
}
void CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result)
{
const Vector3 p = point - p0;
Vector3 n = p1 - p0;
const float length = n.Length();
if (length < 1e-10f)
{
result = p0;
return;
}
n /= length;
const float dot = Vector3::Dot(n, p);
if (dot <= 0.0f)
{
result = p0;
return;
}
if (dot >= length)
{
result = p1;
return;
}
result = p0 + n * dot;
}
Vector3 CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1)
{
Vector3 result;
ClosestPointPointLine(point, p0, p1, result);
return result;
}
void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result) void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result)
{ {
// Source: Real-Time Collision Detection by Christer Ericson // Source: Real-Time Collision Detection by Christer Ericson
@@ -101,6 +140,13 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec
result = vertex1 + ab * v2 + ac * w2; //= u*vertex1 + v*vertex2 + w*vertex3, u = va * denom = 1.0f - v - w result = vertex1 + ab * v2 + ac * w2; //= u*vertex1 + v*vertex2 + w*vertex3, u = va * denom = 1.0f - v - w
} }
Vector3 CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3)
{
Vector3 result;
ClosestPointPointTriangle(point, vertex1, vertex2, vertex3, result);
return result;
}
void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result) void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result)
{ {
// Source: Real-Time Collision Detection by Christer Ericson // Source: Real-Time Collision Detection by Christer Ericson
@@ -112,6 +158,13 @@ void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3&
result = point - t * plane.Normal; result = point - t * plane.Normal;
} }
Vector3 CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point)
{
Vector3 result;
ClosestPointPlanePoint(plane, point, result);
return result;
}
void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result) void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result)
{ {
// Source: Real-Time Collision Detection by Christer Ericson // Source: Real-Time Collision Detection by Christer Ericson
@@ -122,6 +175,13 @@ void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector
Vector3::Min(temp, box.Maximum, result); Vector3::Min(temp, box.Maximum, result);
} }
Vector3 CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point)
{
Vector3 result;
ClosestPointBoxPoint(box, point, result);
return result;
}
void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result) void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result)
{ {
Vector2 temp, end; Vector2 temp, end;
@@ -130,6 +190,13 @@ void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const V
Vector2::Min(temp, end, result); Vector2::Min(temp, end, result);
} }
Vector2 CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point)
{
Vector2 result;
ClosestPointRectanglePoint(rect, point, result);
return result;
}
void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result) void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result)
{ {
// Source: Jorgy343 // Source: Jorgy343
@@ -147,6 +214,13 @@ void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, con
result += sphere.Center; result += sphere.Center;
} }
Vector3 CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point)
{
Vector3 result;
ClosestPointSpherePoint(sphere, point, result);
return result;
}
void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result) void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result)
{ {
// Source: Jorgy343 // Source: Jorgy343
@@ -164,6 +238,13 @@ void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, c
result += sphere1.Center; result += sphere1.Center;
} }
Vector3 CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
{
Vector3 result;
ClosestPointSphereSphere(sphere1, sphere2, result);
return result;
}
float CollisionsHelper::DistancePlanePoint(const Plane& plane, const Vector3& point) float CollisionsHelper::DistancePlanePoint(const Plane& plane, const Vector3& point)
{ {
// Source: Real-Time Collision Detection by Christer Ericson // Source: Real-Time Collision Detection by Christer Ericson
@@ -821,7 +902,6 @@ bool CollisionsHelper::RayIntersectsBox(const Ray& ray, const BoundingBox& box,
d = Math::Abs(size.Z - Math::Abs(localPoint.Z)); d = Math::Abs(size.Z - Math::Abs(localPoint.Z));
if (d < dMin) if (d < dMin)
{ {
dMin = d;
normal = Vector3(0, 0, Math::Sign(localPoint.Z)); normal = Vector3(0, 0, Math::Sign(localPoint.Z));
} }
@@ -990,15 +1070,11 @@ PlaneIntersectionType CollisionsHelper::PlaneIntersectsBox(const Plane& plane, c
min.Z = plane.Normal.Z >= 0.0f ? box.Maximum.Z : box.Minimum.Z; min.Z = plane.Normal.Z >= 0.0f ? box.Maximum.Z : box.Minimum.Z;
float distance = Vector3::Dot(plane.Normal, max); float distance = Vector3::Dot(plane.Normal, max);
if (distance + plane.D > Plane::DistanceEpsilon) if (distance + plane.D > Plane::DistanceEpsilon)
return PlaneIntersectionType::Front; return PlaneIntersectionType::Front;
distance = Vector3::Dot(plane.Normal, min); distance = Vector3::Dot(plane.Normal, min);
if (distance + plane.D < Plane::DistanceEpsilon) if (distance + plane.D < Plane::DistanceEpsilon)
return PlaneIntersectionType::Back; return PlaneIntersectionType::Back;
return PlaneIntersectionType::Intersecting; return PlaneIntersectionType::Intersecting;
} }
@@ -1012,10 +1088,8 @@ PlaneIntersectionType CollisionsHelper::PlaneIntersectsSphere(const Plane& plane
if (distance > sphere.Radius) if (distance > sphere.Radius)
return PlaneIntersectionType::Front; return PlaneIntersectionType::Front;
if (distance < -sphere.Radius) if (distance < -sphere.Radius)
return PlaneIntersectionType::Back; return PlaneIntersectionType::Back;
return PlaneIntersectionType::Intersecting; return PlaneIntersectionType::Intersecting;
} }
@@ -1023,13 +1097,10 @@ bool CollisionsHelper::BoxIntersectsBox(const BoundingBox& box1, const BoundingB
{ {
if (box1.Minimum.X > box2.Maximum.X || box2.Minimum.X > box1.Maximum.X) if (box1.Minimum.X > box2.Maximum.X || box2.Minimum.X > box1.Maximum.X)
return false; return false;
if (box1.Minimum.Y > box2.Maximum.Y || box2.Minimum.Y > box1.Maximum.Y) if (box1.Minimum.Y > box2.Maximum.Y || box2.Minimum.Y > box1.Maximum.Y)
return false; return false;
if (box1.Minimum.Z > box2.Maximum.Z || box2.Minimum.Z > box1.Maximum.Z) if (box1.Minimum.Z > box2.Maximum.Z || box2.Minimum.Z > box1.Maximum.Z)
return false; return false;
return true; return true;
} }

View File

@@ -72,6 +72,33 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param> /// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1, Vector2& result); static void ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1, Vector2& result);
/// <summary>
/// Determines the closest point between a point and a line.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="p0">The line first point.</param>
/// <param name="p1">The line second point.</param>
/// <returns>The closest point between the two objects.</result>
static Vector2 ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1);
/// <summary>
/// Determines the closest point between a point and a line.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="p0">The line first point.</param>
/// <param name="p1">The line second point.</param>
/// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result);
/// <summary>
/// Determines the closest point between a point and a line.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="p0">The line first point.</param>
/// <param name="p1">The line second point.</param>
/// <returns>The closest point between the two objects.</result>
static Vector3 ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1);
/// <summary> /// <summary>
/// Determines the closest point between a point and a triangle. /// Determines the closest point between a point and a triangle.
/// </summary> /// </summary>
@@ -82,6 +109,16 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param> /// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result); static void ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result);
/// <summary>
/// Determines the closest point between a point and a triangle.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="vertex1">The first vertex to test.</param>
/// <param name="vertex2">The second vertex to test.</param>
/// <param name="vertex3">The third vertex to test.</param>
/// <returns>The closest point between the two objects.</returns>
static Vector3 ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3);
/// <summary> /// <summary>
/// Determines the closest point between a <see cref="Plane" /> and a point. /// Determines the closest point between a <see cref="Plane" /> and a point.
/// </summary> /// </summary>
@@ -90,6 +127,14 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param> /// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result); static void ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result);
/// <summary>
/// Determines the closest point between a <see cref="Plane" /> and a point.
/// </summary>
/// <param name="plane">The plane to test.</param>
/// <param name="point">The point to test.</param>
/// <returns>The closest point between the two objects.</returns>
static Vector3 ClosestPointPlanePoint(const Plane& plane, const Vector3& point);
/// <summary> /// <summary>
/// Determines the closest point between a <see cref="BoundingBox" /> and a point. /// Determines the closest point between a <see cref="BoundingBox" /> and a point.
/// </summary> /// </summary>
@@ -98,6 +143,14 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param> /// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result); static void ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result);
/// <summary>
/// Determines the closest point between a <see cref="BoundingBox" /> and a point.
/// </summary>
/// <param name="box">The box to test.</param>
/// <param name="point">The point to test.</param>
/// <returns>The closest point between the two objects.</returns>
static Vector3 ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point);
/// <summary> /// <summary>
/// Determines the closest point between a <see cref="Rectangle" /> and a point. /// Determines the closest point between a <see cref="Rectangle" /> and a point.
/// </summary> /// </summary>
@@ -106,6 +159,14 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param> /// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result); static void ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result);
/// <summary>
/// Determines the closest point between a <see cref="Rectangle" /> and a point.
/// </summary>
/// <param name="rect">The rectangle to test.</param>
/// <param name="point">The point to test.</param>
/// <returns>The closest point between the two objects.</returns>
static Vector2 ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point);
/// <summary> /// <summary>
/// Determines the closest point between a <see cref="BoundingSphere" /> and a point. /// Determines the closest point between a <see cref="BoundingSphere" /> and a point.
/// </summary> /// </summary>
@@ -114,6 +175,14 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects; or, if the point is directly in the center of the sphere, contains <see cref="Vector3.Zero" />.</param> /// <param name="result">When the method completes, contains the closest point between the two objects; or, if the point is directly in the center of the sphere, contains <see cref="Vector3.Zero" />.</param>
static void ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result); static void ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result);
/// <summary>
/// Determines the closest point between a <see cref="BoundingSphere" /> and a point.
/// </summary>
/// <param name="sphere">The sphere to test.</param>
/// <param name="point">The point to test.</param>
/// <returns>The closest point between the two objects; or, if the point is directly in the center of the sphere, contains <see cref="Vector3.Zero" />.</return>
static Vector3 ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point);
/// <summary> /// <summary>
/// Determines the closest point between a <see cref="BoundingSphere" /> and a <see cref="BoundingSphere" />. /// Determines the closest point between a <see cref="BoundingSphere" /> and a <see cref="BoundingSphere" />.
/// </summary> /// </summary>
@@ -127,6 +196,19 @@ public:
/// </remarks> /// </remarks>
static void ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result); static void ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result);
/// <summary>
/// Determines the closest point between a <see cref="BoundingSphere" /> and a <see cref="BoundingSphere" />.
/// </summary>
/// <param name="sphere1">The first sphere to test.</param>
/// <param name="sphere2">The second sphere to test.</param>
/// <returns>The closest point between the two objects; or, if the point is directly in the center of the sphere, contains <see cref="Vector3.Zero" />.</returns>
/// <remarks>
/// If the two spheres are overlapping, but not directly on top of each other, the closest point
/// is the 'closest' point of intersection. This can also be considered is the deepest point of
/// intersection.
/// </remarks>
static Vector3 ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2);
/// <summary> /// <summary>
/// Determines the distance between a <see cref="Plane" /> and a point. /// Determines the distance between a <see cref="Plane" /> and a point.
/// </summary> /// </summary>

View File

@@ -117,7 +117,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay. /// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary> /// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param> /// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param> /// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
@@ -163,7 +163,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay. /// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary> /// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param> /// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param> /// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
@@ -210,7 +210,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay. /// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary> /// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param> /// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param> /// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
@@ -257,7 +257,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay. /// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary> /// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param> /// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param> /// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>

View File

@@ -10,6 +10,127 @@
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h> #include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
namespace
{
template<typename IndexType>
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, Vector3* vertices, IndexType* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors)
{
auto model = mesh->GetModel();
CHECK_RETURN(model && model->IsVirtual(), true);
CHECK_RETURN(triangles && vertices, true);
// Pack mesh data into vertex buffers
Array<VB1ElementType> vb1;
Array<VB2ElementType> vb2;
vb1.Resize(vertexCount);
if (normals)
{
if (tangents)
{
for (uint32 i = 0; i < vertexCount; i++)
{
const Vector3 normal = normals[i];
const Vector3 tangent = tangents[i];
// Calculate bitangent sign
Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent));
byte sign = static_cast<byte>(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0);
// Set tangent frame
vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign);
vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0);
}
}
else
{
for (uint32 i = 0; i < vertexCount; i++)
{
const Vector3 normal = normals[i];
// Calculate tangent
Vector3 c1 = Vector3::Cross(normal, Vector3::UnitZ);
Vector3 c2 = Vector3::Cross(normal, Vector3::UnitY);
Vector3 tangent;
if (c1.LengthSquared() > c2.LengthSquared())
tangent = c1;
else
tangent = c2;
// Calculate bitangent sign
Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent));
byte sign = static_cast<byte>(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0);
// Set tangent frame
vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign);
vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0);
}
}
}
else
{
// Set default tangent frame
const auto n = Float1010102(Vector3::UnitZ);
const auto t = Float1010102(Vector3::UnitX);
for (uint32 i = 0; i < vertexCount; i++)
{
vb1[i].Normal = n;
vb1[i].Tangent = t;
}
}
if (uvs)
{
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].TexCoord = Half2(uvs[i]);
}
else
{
auto v = Half2(0, 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].TexCoord = v;
}
{
auto v = Half2(0, 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].LightmapUVs = v;
}
if (colors)
{
vb2.Resize(vertexCount);
for (uint32 i = 0; i < vertexCount; i++)
vb2[i].Color = colors[i];
}
return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vertices, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, triangles);
}
template<typename IndexType>
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
{
ASSERT((uint32)mono_array_length(verticesObj) >= vertexCount);
ASSERT((uint32)mono_array_length(trianglesObj) / 3 >= triangleCount);
auto vertices = (Vector3*)(void*)mono_array_addr_with_size(verticesObj, sizeof(Vector3), 0);
auto triangles = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0);
const auto normals = normalsObj ? (Vector3*)(void*)mono_array_addr_with_size(normalsObj, sizeof(Vector3), 0) : nullptr;
const auto tangents = tangentsObj ? (Vector3*)(void*)mono_array_addr_with_size(tangentsObj, sizeof(Vector3), 0) : nullptr;
const auto uvs = uvObj ? (Vector2*)(void*)mono_array_addr_with_size(uvObj, sizeof(Vector2), 0) : nullptr;
const auto colors = colorsObj ? (Color32*)(void*)mono_array_addr_with_size(colorsObj, sizeof(Color32), 0) : nullptr;
return UpdateMesh<IndexType>(mesh, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
}
template<typename IndexType>
bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MonoArray* trianglesObj)
{
const auto model = mesh->GetModel();
ASSERT(model && model->IsVirtual() && trianglesObj);
// Get buffer data
ASSERT((int32)mono_array_length(trianglesObj) / 3 >= triangleCount);
auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0);
return mesh->UpdateTriangles(triangleCount, ib);
}
}
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices) bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices)
{ {
Unload(); Unload();
@@ -31,6 +152,16 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType*
return failed; return failed;
} }
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint16* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors)
{
return ::UpdateMesh<uint16>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
}
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors)
{
return ::UpdateMesh<uint32>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
}
bool Mesh::UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices) bool Mesh::UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices)
{ {
// Cache data // Cache data
@@ -384,108 +515,9 @@ ScriptingObject* Mesh::GetParentModel()
return _model; return _model;
} }
template<typename IndexType>
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
{
auto model = mesh->GetModel();
ASSERT(model && model->IsVirtual() && verticesObj && trianglesObj);
// Get buffers data
ASSERT((uint32)mono_array_length(verticesObj) >= vertexCount);
ASSERT((uint32)mono_array_length(trianglesObj) / 3 >= triangleCount);
auto vb0 = (Vector3*)(void*)mono_array_addr_with_size(verticesObj, sizeof(Vector3), 0);
auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0);
Array<VB1ElementType> vb1;
Array<VB2ElementType> vb2;
vb1.Resize(vertexCount);
if (normalsObj)
{
const auto normals = (Vector3*)(void*)mono_array_addr_with_size(normalsObj, sizeof(Vector3), 0);
if (tangentsObj)
{
const auto tangents = (Vector3*)(void*)mono_array_addr_with_size(tangentsObj, sizeof(Vector3), 0);
for (uint32 i = 0; i < vertexCount; i++)
{
// Peek normal and tangent
const Vector3 normal = normals[i];
const Vector3 tangent = tangents[i];
// Calculate bitangent sign
Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent));
byte sign = static_cast<byte>(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0);
// Set tangent frame
vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign);
vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0);
}
}
else
{
for (uint32 i = 0; i < vertexCount; i++)
{
// Peek normal
const Vector3 normal = normals[i];
// Calculate tangent
Vector3 c1 = Vector3::Cross(normal, Vector3::UnitZ);
Vector3 c2 = Vector3::Cross(normal, Vector3::UnitY);
Vector3 tangent;
if (c1.LengthSquared() > c2.LengthSquared())
tangent = c1;
else
tangent = c2;
// Calculate bitangent sign
Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent));
byte sign = static_cast<byte>(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0);
// Set tangent frame
vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign);
vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0);
}
}
}
else
{
const auto n = Float1010102(Vector3::UnitZ);
const auto t = Float1010102(Vector3::UnitX);
for (uint32 i = 0; i < vertexCount; i++)
{
vb1[i].Normal = n;
vb1[i].Tangent = t;
}
}
if (uvObj)
{
const auto uvs = (Vector2*)(void*)mono_array_addr_with_size(uvObj, sizeof(Vector2), 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].TexCoord = Half2(uvs[i]);
}
else
{
auto v = Half2(0, 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].TexCoord = v;
}
{
auto v = Half2(0, 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].LightmapUVs = v;
}
if (colorsObj)
{
vb2.Resize(vertexCount);
const auto colors = (Color32*)(void*)mono_array_addr_with_size(colorsObj, sizeof(Color32), 0);
for (uint32 i = 0; i < vertexCount; i++)
vb2[i].Color = colors[i];
}
return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vb0, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, ib);
}
bool Mesh::UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) bool Mesh::UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
{ {
return ::UpdateMesh<int32>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); return ::UpdateMesh<uint32>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
} }
bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
@@ -493,22 +525,9 @@ bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* v
return ::UpdateMesh<uint16>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); return ::UpdateMesh<uint16>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
} }
template<typename IndexType>
bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MonoArray* trianglesObj)
{
auto model = mesh->GetModel();
ASSERT(model && model->IsVirtual() && trianglesObj);
// Get buffer data
ASSERT((int32)mono_array_length(trianglesObj) / 3 >= triangleCount);
auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0);
return mesh->UpdateTriangles(triangleCount, ib);
}
bool Mesh::UpdateTrianglesInt(int32 triangleCount, MonoArray* trianglesObj) bool Mesh::UpdateTrianglesInt(int32 triangleCount, MonoArray* trianglesObj)
{ {
return ::UpdateTriangles<int32>(this, triangleCount, trianglesObj); return ::UpdateTriangles<uint32>(this, triangleCount, trianglesObj);
} }
bool Mesh::UpdateTrianglesUShort(int32 triangleCount, MonoArray* trianglesObj) bool Mesh::UpdateTrianglesUShort(int32 triangleCount, MonoArray* trianglesObj)

View File

@@ -216,9 +216,9 @@ public:
/// <param name="vb0">The first vertex buffer data.</param> /// <param name="vb0">The first vertex buffer data.</param>
/// <param name="vb1">The second vertex buffer data.</param> /// <param name="vb1">The second vertex buffer data.</param>
/// <param name="vb2">The third vertex buffer data.</param> /// <param name="vb2">The third vertex buffer data.</param>
/// <param name="ib">The index buffer.</param> /// <param name="ib">The index buffer in clockwise order.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, int32* ib) FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint32* ib)
{ {
return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, false); return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, false);
} }
@@ -231,7 +231,7 @@ public:
/// <param name="vb0">The first vertex buffer data.</param> /// <param name="vb0">The first vertex buffer data.</param>
/// <param name="vb1">The second vertex buffer data.</param> /// <param name="vb1">The second vertex buffer data.</param>
/// <param name="vb2">The third vertex buffer data.</param> /// <param name="vb2">The third vertex buffer data.</param>
/// <param name="ib">The index buffer.</param> /// <param name="ib">The index buffer in clockwise order.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint16* ib) FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint16* ib)
{ {
@@ -240,17 +240,51 @@ public:
/// <summary> /// <summary>
/// Updates the model mesh (used by the virtual models created with Init rather than Load). /// Updates the model mesh (used by the virtual models created with Init rather than Load).
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary> /// </summary>
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param> /// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param> /// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vb0">The first vertex buffer data.</param> /// <param name="vb0">The first vertex buffer data.</param>
/// <param name="vb1">The second vertex buffer data.</param> /// <param name="vb1">The second vertex buffer data.</param>
/// <param name="vb2">The third vertex buffer data.</param> /// <param name="vb2">The third vertex buffer data.</param>
/// <param name="ib">The index buffer.</param> /// <param name="ib">The index buffer in clockwise order.</param>
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param> /// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices); bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices);
/// <summary>
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uvs">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param>
/// <returns>True if failed, otherwise false.</returns>
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint16* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr);
/// <summary>
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uvs">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param>
/// <returns>True if failed, otherwise false.</returns>
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr);
public: public:
/// <summary> /// <summary>
@@ -259,7 +293,7 @@ public:
/// <param name="triangleCount">The amount of triangles in the index buffer.</param> /// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="ib">The index buffer.</param> /// <param name="ib">The index buffer.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, int32* ib) FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, uint32* ib)
{ {
return UpdateTriangles(triangleCount, ib, false); return UpdateTriangles(triangleCount, ib, false);
} }

View File

@@ -163,7 +163,7 @@ public:
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param> /// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param> /// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vb">The vertex buffer data.</param> /// <param name="vb">The vertex buffer data.</param>
/// <param name="ib">The index buffer.</param> /// <param name="ib">The index buffer in clockwise order.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, int32* ib) FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, int32* ib)
{ {
@@ -176,7 +176,7 @@ public:
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param> /// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param> /// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vb">The vertex buffer data.</param> /// <param name="vb">The vertex buffer data.</param>
/// <param name="ib">The index buffer.</param> /// <param name="ib">The index buffer, clockwise order.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint16* ib) FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint16* ib)
{ {
@@ -189,7 +189,7 @@ public:
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param> /// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param> /// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vb">The vertex buffer data.</param> /// <param name="vb">The vertex buffer data.</param>
/// <param name="ib">The index buffer.</param> /// <param name="ib">The index buffer in clockwise order.</param>
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param> /// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices); bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices);

View File

@@ -104,7 +104,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay. /// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary> /// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param> /// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param> /// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="blendIndices">The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null.</param> /// <param name="blendIndices">The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null.</param>
/// <param name="blendWeights">The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null.</param> /// <param name="blendWeights">The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>
@@ -140,7 +140,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay. /// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary> /// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param> /// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param> /// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="blendIndices">The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null.</param> /// <param name="blendIndices">The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null.</param>
/// <param name="blendWeights">The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null.</param> /// <param name="blendWeights">The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>

View File

@@ -55,7 +55,7 @@ void CharacterController::SetHeight(const float value)
void CharacterController::SetSlopeLimit(float value) void CharacterController::SetSlopeLimit(float value)
{ {
value = Math::Clamp(value, 0.0f, 90.0f); value = Math::Clamp(value, 0.0f, 89.0f);
if (Math::NearEqual(value, _slopeLimit)) if (Math::NearEqual(value, _slopeLimit))
return; return;
@@ -177,7 +177,7 @@ void CharacterController::CreateActor()
const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f; const float minSize = 0.001f;
desc.height = Math::Max(Math::Abs(_height) * scaling, minSize); desc.height = Math::Max(Math::Abs(_height) * scaling, minSize);
desc.radius = Math::Max(Math::Abs(_radius) * scaling, minSize); desc.radius = Math::Max(Math::Abs(_radius) * scaling - desc.contactOffset, minSize);
// Create controller // Create controller
_controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc); _controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc);
@@ -202,7 +202,7 @@ void CharacterController::UpdateSize() const
{ {
const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f; const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize); const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
_controller->setRadius(radius); _controller->setRadius(radius);

View File

@@ -5,6 +5,7 @@
#include "WindowsFileSystem.h" #include "WindowsFileSystem.h"
#include "Engine/Platform/File.h" #include "Engine/Platform/File.h"
#include "Engine/Platform/Window.h" #include "Engine/Platform/Window.h"
#include "Engine/Platform/Windows/ComPtr.h"
#include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/StringView.h"
#include "../Win32/IncludeWindowsHeaders.h" #include "../Win32/IncludeWindowsHeaders.h"
@@ -293,41 +294,39 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin
{ {
bool result = true; bool result = true;
// Allocate memory for the filenames // Randomly generated GUID used for storing the last location of this dialog
int32 maxPathSize = 2 * MAX_PATH; const Guid folderGuid(0x53890ed9, 0xa55e47ba, 0xa970bdae, 0x72acedff);
Array<Char> pathBuffer;
pathBuffer.Resize(maxPathSize);
pathBuffer[0] = 0;
// Setup description ComPtr<IFileOpenDialog> fd;
BROWSEINFOW bi; if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fd))))
ZeroMemory(&bi, sizeof(bi));
if (parentWindow)
bi.hwndOwner = static_cast<HWND>(parentWindow->GetNativePtr());
bi.lpszTitle = title.HasChars() ? title.Get() : nullptr;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)(initialDirectory.HasChars() ? initialDirectory.Get() : nullptr);
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
if (pidl != nullptr)
{ {
// Get the name of the folder and put it in path DWORD options;
SHGetPathFromIDList(pidl, pathBuffer.Get()); fd->GetOptions(&options);
fd->SetOptions(options | FOS_PICKFOLDERS | FOS_NOCHANGEDIR);
if (pathBuffer[0] != 0) if (title.HasChars())
{ fd->SetTitle(title.Get());
path = pathBuffer.Get();
result = false;
}
// Free memory used // Associate the last selected folder with this GUID instead of overwriting the global one
IMalloc* imalloc = 0; fd->SetClientGuid(*reinterpret_cast<const GUID*>(&folderGuid));
if (SUCCEEDED(SHGetMalloc(&imalloc)))
ComPtr<IShellItem> defaultFolder;
if (SUCCEEDED(SHCreateItemFromParsingName(initialDirectory.Get(), NULL, IID_PPV_ARGS(&defaultFolder))))
fd->SetFolder(defaultFolder);
if (SUCCEEDED(fd->Show(parentWindow->GetHWND())))
{ {
imalloc->Free(pidl); ComPtr<IShellItem> si;
imalloc->Release(); if (SUCCEEDED(fd->GetResult(&si)))
{
LPWSTR resultPath;
if (SUCCEEDED(si->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &resultPath)))
{
path = resultPath;
CoTaskMemFree(resultPath);
result = false;
}
}
} }
} }

View File

@@ -274,8 +274,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
// Compute depth difference // Compute depth difference
auto depthDiff = writeLocal(VariantType::Float, String::Format(TEXT("{0} * ViewFar - {1}"), sceneDepth.Value, posVS.Value), node); auto depthDiff = writeLocal(VariantType::Float, String::Format(TEXT("{0} * ViewFar - {1}"), sceneDepth.Value, posVS.Value), node);
auto fadeDistance = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat();
// Apply smoothing factor and clamp the result // Apply smoothing factor and clamp the result
value = writeLocal(VariantType::Float, String::Format(TEXT("saturate({0} / {1})"), depthDiff.Value, node->Values[0].AsFloat), node); value = writeLocal(VariantType::Float, String::Format(TEXT("saturate({0} / {1})"), depthDiff.Value, fadeDistance.Value), node);
break; break;
} }
// Material Function // Material Function
@@ -337,6 +339,59 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
case 25: case 25:
value = Value(VariantType::Vector3, TEXT("GetObjectSize(input)")); value = Value(VariantType::Vector3, TEXT("GetObjectSize(input)"));
break; break;
// Blend Normals
case 26:
{
const auto baseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3();
const auto additionalNormal = tryGetValue(node->GetBox(1), Value::Zero).AsVector3();
const String text = String::Format(TEXT("float3((float2({0}.xy) + float2({1}.xy) * 2.0), sqrt(saturate(1.0 - dot((float2({0}.xy) + float2({1}.xy) * 2.0).xy, (float2({0}.xy) + float2({1}.xy) * 2.0).xy))))"), baseNormal.Value, additionalNormal.Value);
value = writeLocal(ValueType::Vector3, text, node);
break;
}
// Rotator
case 27:
{
auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2();
auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2();
auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat();
const auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, uv.Value), node);
const auto raCosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node);
const auto dotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), raCosSin.Value), node);
const auto dotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), raCosSin.Value), node);
value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, dotB1.Value, dotB2.Value, center.Value), node);
break;
}
// Sphere Mask
case 28:
{
Value a = tryGetValue(node->GetBox(0), 0, Value::Zero);
Value b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type);
Value radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat();
Value hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat();
Value invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool();
// Get distance and apply radius
auto x1 = writeLocal(ValueType::Float, String::Format(TEXT("distance({0},{1}) * (1 / {2})"), a.Value, b.Value, radius.Value), node);
// Apply hardness, use 0.991 as max since any value above will result in harsh aliasing
auto x2 = writeLocal(ValueType::Float, String::Format(TEXT("saturate((1 - {0}) * (1 / (1 - clamp({1}, 0, 0.991f))))"), x1.Value, hardness.Value), node);
value = writeLocal(ValueType::Float, String::Format(TEXT("{0} ? (1 - {1}) : {1}"), invert.Value, x2.Value), node);
break;
}
// Tiling & Offset
case 29:
{
auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2();
auto tiling = tryGetValue(node->GetBox(1), node->Values[0]).AsVector2();
auto offset = tryGetValue(node->GetBox(2), node->Values[1]).AsVector2();
value = writeLocal(ValueType::Vector2, String::Format(TEXT("{0} * {1} + {2}"), uv.Value, tiling.Value, offset.Value), node);
break;
}
default: default:
break; break;
} }

View File

@@ -190,6 +190,16 @@ bool TextureTool::ExportTextureDirectXTex(ImageType type, const StringView& path
} }
img = tmp.GetImage(0, 0, 0); img = tmp.GetImage(0, 0, 0);
} }
else if (image.format == DXGI_FORMAT_R10G10B10A2_UNORM || image.format == DXGI_FORMAT_R11G11B10_FLOAT)
{
result = DirectX::Convert(image, DXGI_FORMAT_R8G8B8A8_UNORM, DirectX::TEX_FILTER_DEFAULT, DirectX::TEX_THRESHOLD_DEFAULT, tmp);
if (FAILED(result))
{
LOG(Error, "Cannot convert texture, error: {0:x}", static_cast<uint32>(result));
return true;
}
img = tmp.GetImage(0, 0, 0);
}
DirectX::WICCodecs codec; DirectX::WICCodecs codec;
switch (type) switch (type)

View File

@@ -401,6 +401,18 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
{ {
value = writeFunction1(node, tryGetValue(node->GetBox(0), Value::Zero), TEXT("radians")); value = writeFunction1(node, tryGetValue(node->GetBox(0), Value::Zero), TEXT("radians"));
break; break;
}
// Remap
case 48:
{
const auto inVal = tryGetValue(node->GetBox(0), node->Values[0].AsFloat);
const auto rangeA = tryGetValue(node->GetBox(1), node->Values[1].AsVector2());
const auto rangeB = tryGetValue(node->GetBox(2), node->Values[2].AsVector2());
const auto clamp = tryGetValue(node->GetBox(3), node->Values[3]).AsBool();
const auto mapFunc = String::Format(TEXT("{2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x)"), inVal.Value, rangeA.Value, rangeB.Value);
value = writeLocal(ValueType::Float, String::Format(TEXT("{2} ? clamp({0}, {1}.x, {1}.y) : {0}"), mapFunc, rangeB.Value, clamp.Value), node);
break;
} }
default: default:
break; break;

View File

@@ -371,6 +371,20 @@ void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value)
if (value.Type.Type == VariantType::Enum) if (value.Type.Type == VariantType::Enum)
value.AsUint64 = value.AsUint64 | (uint64)tryGetValue(node->GetBox(1), Value::Zero); value.AsUint64 = value.AsUint64 | (uint64)tryGetValue(node->GetBox(1), Value::Zero);
break; break;
// Remap
case 48:
{
const float inVal = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat;
const Vector2 rangeA = tryGetValue(node->GetBox(1), node->Values[1]).AsVector2();
const Vector2 rangeB = tryGetValue(node->GetBox(2), node->Values[2]).AsVector2();
const bool clamp = tryGetValue(node->GetBox(3), node->Values[3]).AsBool;
auto mapFunc = rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X);
// Clamp value?
value = clamp ? Math::Clamp(mapFunc, rangeB.X, rangeB.Y) : mapFunc;
break;
}
default: default:
break; break;
} }

View File

@@ -56,6 +56,11 @@ namespace Flax.Build.NativeCpp
/// </summary> /// </summary>
public bool Optimization = false; public bool Optimization = false;
/// <summary>
/// Enables the whole program optimization.
/// </summary>
public bool WholeProgramOptimization = false;
/// <summary> /// <summary>
/// Enables functions level linking support. /// Enables functions level linking support.
/// </summary> /// </summary>
@@ -131,6 +136,7 @@ namespace Flax.Build.NativeCpp
RuntimeTypeInfo = RuntimeTypeInfo, RuntimeTypeInfo = RuntimeTypeInfo,
Inlining = Inlining, Inlining = Inlining,
Optimization = Optimization, Optimization = Optimization,
WholeProgramOptimization = WholeProgramOptimization,
FunctionLevelLinking = FunctionLevelLinking, FunctionLevelLinking = FunctionLevelLinking,
DebugInformation = DebugInformation, DebugInformation = DebugInformation,
UseDebugCRT = UseDebugCRT, UseDebugCRT = UseDebugCRT,

View File

@@ -256,6 +256,7 @@ namespace Flax.Build
options.CompileEnv.IntrinsicFunctions = false; options.CompileEnv.IntrinsicFunctions = false;
options.CompileEnv.BufferSecurityCheck = true; options.CompileEnv.BufferSecurityCheck = true;
options.CompileEnv.Inlining = false; options.CompileEnv.Inlining = false;
options.CompileEnv.WholeProgramOptimization = false;
options.LinkEnv.DebugInformation = true; options.LinkEnv.DebugInformation = true;
options.LinkEnv.LinkTimeCodeGeneration = false; options.LinkEnv.LinkTimeCodeGeneration = false;
@@ -273,11 +274,11 @@ namespace Flax.Build
options.CompileEnv.IntrinsicFunctions = true; options.CompileEnv.IntrinsicFunctions = true;
options.CompileEnv.BufferSecurityCheck = true; options.CompileEnv.BufferSecurityCheck = true;
options.CompileEnv.Inlining = true; options.CompileEnv.Inlining = true;
//options.CompileEnv.WholeProgramOptimization = true; options.CompileEnv.WholeProgramOptimization = false;
options.LinkEnv.DebugInformation = true; options.LinkEnv.DebugInformation = true;
options.LinkEnv.LinkTimeCodeGeneration = true; options.LinkEnv.LinkTimeCodeGeneration = false;
options.LinkEnv.UseIncrementalLinking = false; options.LinkEnv.UseIncrementalLinking = true;
options.LinkEnv.Optimization = true; options.LinkEnv.Optimization = true;
break; break;
case TargetConfiguration.Release: case TargetConfiguration.Release:
@@ -291,7 +292,7 @@ namespace Flax.Build
options.CompileEnv.IntrinsicFunctions = true; options.CompileEnv.IntrinsicFunctions = true;
options.CompileEnv.BufferSecurityCheck = false; options.CompileEnv.BufferSecurityCheck = false;
options.CompileEnv.Inlining = true; options.CompileEnv.Inlining = true;
//options.CompileEnv.WholeProgramOptimization = true; options.CompileEnv.WholeProgramOptimization = true;
options.LinkEnv.DebugInformation = false; options.LinkEnv.DebugInformation = false;
options.LinkEnv.LinkTimeCodeGeneration = true; options.LinkEnv.LinkTimeCodeGeneration = true;

View File

@@ -467,8 +467,11 @@ namespace Flax.Build.Platforms
// Frame-Pointer Omission // Frame-Pointer Omission
commonArgs.Add("/Oy"); commonArgs.Add("/Oy");
// Whole Program Optimization if (compileEnvironment.WholeProgramOptimization)
commonArgs.Add("/GL"); {
// Whole Program Optimization
commonArgs.Add("/GL");
}
} }
else else
{ {
@@ -721,7 +724,7 @@ namespace Flax.Build.Platforms
args.Add("/PDBALTPATH:%_PDB%"); args.Add("/PDBALTPATH:%_PDB%");
// Optimize // Optimize
if (linkEnvironment.Optimization) if (linkEnvironment.Optimization && !linkEnvironment.UseIncrementalLinking)
{ {
// Generate an EXE checksum // Generate an EXE checksum
args.Add("/RELEASE"); args.Add("/RELEASE");