Merge branch '1.1' of https://github.com/FlaxEngine/FlaxEngine into ortho
Conflicts: Source/Editor/Viewport/EditorViewport.cs
This commit is contained in:
14
Development/Documentation/mono.md
Normal file
14
Development/Documentation/mono.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## Mono
|
||||
|
||||
Custom fork: [https://github.com/FlaxEngine/mono](https://github.com/FlaxEngine/mono) with custom features for C# assemblies hot-reloading at runtime without domain unload (more: [https://flaxengine.com/blog/flax-facts-16-scripts-hot-reload/](https://flaxengine.com/blog/flax-facts-16-scripts-hot-reload/)).
|
||||
|
||||
### Notes
|
||||
|
||||
Some useful notes and tips for devs:
|
||||
* When working with mono fork set `localRepoPath` to local repo location in `Source\Tools\Flax.Build\Deps\Dependencies\mono.cs`
|
||||
* To update mono deps when developing/updating use `.\Development\Scripts\Windows\CallBuildTool.bat -log -ReBuildDeps -verbose -depsToBuild=mono -platform=Windows`, then build engine and run it
|
||||
* `MONO_GC_DEBUG=check-remset-consistency` - it will do additional checks at each collection to see if there are any missing write barriers
|
||||
* `MONO_GC_DEBUG=nursery-canaries` - it might catch some buffer overflows in case of problems in code.
|
||||
* `MONO_GC_DEBUG=<log-level>:<log-file>` - will print GC debug to the log file (eg. `4:sgen-gc`).
|
||||
* Methods `mono_custom_attrs_from_property` and `mono_custom_attrs_get_attr` are internally cached
|
||||
* If C++ mono call a method in c# that will throw an error, error will be handled but, not completly. Calling relase domain will return random `Access memory violation`. First search for error in c# code. No workaround yet.
|
||||
@@ -1081,12 +1081,12 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
|
||||
data.StepProgress(TEXT("Creating assets cache"), Step2ProgressEnd);
|
||||
|
||||
// Create asset paths mapping for the root assets.
|
||||
// Create asset paths mapping for the assets.
|
||||
// Assets mapping is use to convert paths used in Content::Load(path) into the asset id.
|
||||
// It fixes the issues when in build game all assets are in the packages while engine parts are requesting in-build assets by name.
|
||||
// It fixes the issues when in build game all assets are in the packages and are requested by path.
|
||||
// E.g. game settings are loaded from `Content/GameSettings.json` file which is packages in one of the packages.
|
||||
// Additionally it improves the in-build assets loading performance.
|
||||
for (auto i = data.RootAssets.Begin(); i.IsNotEnd(); ++i)
|
||||
// Additionally it improves the in-build assets loading performance (no more registry linear lookup for path by dictionary access).
|
||||
for (auto i = data.Assets.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (Content::GetAssetInfo(i->Item, assetInfo))
|
||||
{
|
||||
|
||||
@@ -43,10 +43,18 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Copy old values
|
||||
Array.Copy(array, 0, newValues, 0, sharedCount);
|
||||
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
if (elementType.IsValueType)
|
||||
{
|
||||
Array.Copy(array, oldSize - 1, newValues, i, 1);
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
Array.Copy(array, oldSize - 1, newValues, i, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize new entries with default values
|
||||
var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType));
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
newValues.SetValue(defaultValue, i);
|
||||
}
|
||||
}
|
||||
else if (newSize > 0)
|
||||
@@ -54,9 +62,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Initialize new entries with default values
|
||||
var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType));
|
||||
for (int i = 0; i < newSize; i++)
|
||||
{
|
||||
newValues.SetValue(defaultValue, i);
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
|
||||
@@ -434,17 +434,19 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Create a new instance of the object",
|
||||
Height = ButtonSize,
|
||||
Width = ButtonSize,
|
||||
X = layout.ContainerControl.Width - ButtonSize - 4,
|
||||
Size = new Vector2(ButtonSize, ButtonSize),
|
||||
AnchorPreset = AnchorPresets.MiddleRight,
|
||||
Parent = layout.ContainerControl
|
||||
Parent = layout.ContainerControl,
|
||||
Location = new Vector2(layout.ContainerControl.Width - ButtonSize - 4, (layout.ContainerControl.Height - ButtonSize) * 0.5f),
|
||||
};
|
||||
button.Clicked += () =>
|
||||
{
|
||||
var newType = Values.Type;
|
||||
SetValue(newType.CreateInstance());
|
||||
RebuildLayoutOnRefresh();
|
||||
if (ParentEditor != null)
|
||||
ParentEditor.RebuildLayoutOnRefresh();
|
||||
else
|
||||
RebuildLayoutOnRefresh();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -40,30 +40,35 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Allocate new list
|
||||
var listType = Values.Type;
|
||||
var newValues = (IList)listType.CreateInstance();
|
||||
var elementType = ElementType;
|
||||
|
||||
var sharedCount = Mathf.Min(oldSize, newSize);
|
||||
if (list != null && sharedCount > 0)
|
||||
{
|
||||
// Copy old values
|
||||
for (int i = 0; i < sharedCount; i++)
|
||||
{
|
||||
newValues.Add(list[i]);
|
||||
}
|
||||
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
if (elementType.IsValueType)
|
||||
{
|
||||
newValues.Add(list[oldSize - 1]);
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
newValues.Add(list[oldSize - 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize new entries with default values
|
||||
var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType);
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
newValues.Add(defaultValue);
|
||||
}
|
||||
}
|
||||
else if (newSize > 0)
|
||||
{
|
||||
// Fill new entries with default value
|
||||
var defaultValue = Scripting.TypeUtils.GetDefaultValue(ElementType);
|
||||
var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType);
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
{
|
||||
newValues.Add(defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
|
||||
@@ -983,6 +983,103 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
private class RerouteNode : SurfaceNode
|
||||
{
|
||||
public static readonly Vector2 DefaultSize = new Vector2(16);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool ShowTooltip => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public RerouteNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
Size = DefaultSize;
|
||||
Title = string.Empty;
|
||||
BackgroundColor = Color.Transparent;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSurfaceLoaded()
|
||||
{
|
||||
base.OnSurfaceLoaded();
|
||||
|
||||
var inputBox = GetBox(0);
|
||||
var outputBox = GetBox(1);
|
||||
inputBox.Location = Vector2.Zero;
|
||||
outputBox.Location = Vector2.Zero;
|
||||
|
||||
UpdateBoxes();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ConnectionTick(Box box)
|
||||
{
|
||||
base.ConnectionTick(box);
|
||||
|
||||
UpdateBoxes();
|
||||
}
|
||||
|
||||
private void UpdateBoxes()
|
||||
{
|
||||
var inputBox = GetBox(0);
|
||||
var outputBox = GetBox(1);
|
||||
|
||||
inputBox.Visible = !inputBox.HasAnyConnection;
|
||||
outputBox.Visible = !outputBox.HasAnyConnection;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSelect(ref Vector2 location)
|
||||
{
|
||||
return new Rectangle(Location, DefaultSize).Contains(ref location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateRectangles()
|
||||
{
|
||||
_headerRect = Rectangle.Empty;
|
||||
_closeButtonRect = Rectangle.Empty;
|
||||
_footerRect = Rectangle.Empty;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Surface.Style;
|
||||
var inputBox = GetBox(0);
|
||||
var outputBox = GetBox(1);
|
||||
var connectionColor = style.Colors.Default;
|
||||
float barHorizontalOffset = -2;
|
||||
float barHeight = 3;
|
||||
|
||||
if (inputBox.HasAnyConnection)
|
||||
{
|
||||
var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints;
|
||||
Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor);
|
||||
}
|
||||
|
||||
if (!inputBox.HasAnyConnection)
|
||||
{
|
||||
Render2D.FillRectangle(new Rectangle(-barHorizontalOffset - barHeight * 2, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor);
|
||||
}
|
||||
|
||||
if (!outputBox.HasAnyConnection)
|
||||
{
|
||||
Render2D.FillRectangle(new Rectangle(DefaultSize.X + barHorizontalOffset, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor);
|
||||
}
|
||||
|
||||
if (inputBox.HasAnyConnection && outputBox.HasAnyConnection)
|
||||
{
|
||||
var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints;
|
||||
Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor);
|
||||
SpriteHandle icon = style.Icons.BoxClose;
|
||||
Render2D.DrawSprite(icon, new Rectangle(Vector2.Zero, DefaultSize), connectionColor);
|
||||
}
|
||||
|
||||
base.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The nodes for that group.
|
||||
/// </summary>
|
||||
@@ -1438,6 +1535,23 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(object), 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 29,
|
||||
Title = "Reroute",
|
||||
Create = (id, context, arch, groupArch) => new RerouteNode(id, context, arch, groupArch),
|
||||
Description = "Reroute a connection.",
|
||||
Flags = NodeFlags.NoCloseButton | NodeFlags.NoSpawnViaGUI | NodeFlags.AllGraphs,
|
||||
Size = RerouteNode.DefaultSize,
|
||||
ConnectionsHints = ConnectionsHint.All,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
DependentBoxes = new int[] { 1 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1, true),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
var start = font.GetCharPosition(_archetype.Title, 0);
|
||||
var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1);
|
||||
_highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height));
|
||||
_isFullMatch = true;
|
||||
Visible = true;
|
||||
}
|
||||
else if (NodeArchetype.TryParseText != null && NodeArchetype.TryParseText(filterText, out var data))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using System;
|
||||
|
||||
namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
@@ -12,6 +13,11 @@ namespace FlaxEditor.Surface.Elements
|
||||
[HideInEditor]
|
||||
public class OutputBox : Box
|
||||
{
|
||||
/// <summary>
|
||||
/// Distance for the mouse to be considered above the connection
|
||||
/// </summary>
|
||||
public float MouseOverConnectionDistance => 100f / Surface.ViewScale;
|
||||
|
||||
/// <inheritdoc />
|
||||
public OutputBox(SurfaceNode parentNode, NodeElementArchetype archetype)
|
||||
: base(parentNode, archetype, archetype.Position + new Vector2(parentNode.Archetype.Size.X, 0))
|
||||
@@ -43,23 +49,96 @@ namespace FlaxEditor.Surface.Elements
|
||||
*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point intersects a connection
|
||||
/// </summary>
|
||||
/// <param name="targetBox">The other box.</param>
|
||||
/// <param name="mousePosition">The mouse position</param>
|
||||
public bool IntersectsConnection(Box targetBox, ref Vector2 mousePosition)
|
||||
{
|
||||
var startPos = Parent.PointToParent(Center);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center);
|
||||
return IntersectsConnection(ref startPos, ref endPos, ref mousePosition, MouseOverConnectionDistance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a point intersects a bezier curve
|
||||
/// </summary>
|
||||
/// <param name="start">The start location.</param>
|
||||
/// <param name="end">The end location.</param>
|
||||
/// <param name="point">The point</param>
|
||||
/// <param name="distance">Distance at which its an intersection</param>
|
||||
public static bool IntersectsConnection(ref Vector2 start, ref Vector2 end, ref Vector2 point, float distance)
|
||||
{
|
||||
// Pretty much a point in rectangle check
|
||||
if ((point.X - start.X) * (end.X - point.X) < 0) return false;
|
||||
|
||||
float offset = Mathf.Sign(end.Y - start.Y) * distance;
|
||||
if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0) return false;
|
||||
|
||||
// Taken from the Render2D.DrawBezier code
|
||||
float squaredDistance = distance;
|
||||
|
||||
var dst = (end - start) * new Vector2(0.5f, 0.05f);
|
||||
Vector2 control1 = new Vector2(start.X + dst.X, start.Y + dst.Y);
|
||||
Vector2 control2 = new Vector2(end.X - dst.X, end.Y + dst.Y);
|
||||
|
||||
Vector2 d1 = control1 - start;
|
||||
Vector2 d2 = control2 - control1;
|
||||
Vector2 d3 = end - control2;
|
||||
float len = d1.Length + d2.Length + d3.Length;
|
||||
int segmentCount = Math.Min(Math.Max(Mathf.CeilToInt(len * 0.05f), 1), 100);
|
||||
float segmentCountInv = 1.0f / segmentCount;
|
||||
|
||||
Bezier(ref start, ref control1, ref control2, ref end, 0, out Vector2 p);
|
||||
for (int i = 1; i <= segmentCount; i++)
|
||||
{
|
||||
Vector2 oldp = p;
|
||||
float t = i * segmentCountInv;
|
||||
Bezier(ref start, ref control1, ref control2, ref end, t, out p);
|
||||
|
||||
// Maybe it would be reasonable to return the point?
|
||||
CollisionsHelper.ClosestPointPointLine(ref point, ref oldp, ref p, out Vector2 result);
|
||||
if (Vector2.DistanceSquared(point, result) <= squaredDistance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void Bezier(ref Vector2 p0, ref Vector2 p1, ref Vector2 p2, ref Vector2 p3, float alpha, out Vector2 result)
|
||||
{
|
||||
Vector2.Lerp(ref p0, ref p1, alpha, out var p01);
|
||||
Vector2.Lerp(ref p1, ref p2, alpha, out var p12);
|
||||
Vector2.Lerp(ref p2, ref p3, alpha, out var p23);
|
||||
Vector2.Lerp(ref p01, ref p12, alpha, out var p012);
|
||||
Vector2.Lerp(ref p12, ref p23, alpha, out var p123);
|
||||
Vector2.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw all connections coming from this box.
|
||||
/// </summary>
|
||||
public void DrawConnections()
|
||||
public void DrawConnections(ref Vector2 mousePosition)
|
||||
{
|
||||
float mouseOverDistance = MouseOverConnectionDistance;
|
||||
// Draw all the connections
|
||||
var center = Size * 0.5f;
|
||||
var tmp = PointToParent(ref center);
|
||||
var startPos = Parent.PointToParent(ref tmp);
|
||||
var startPos = Parent.PointToParent(Center);
|
||||
var startHighlight = ConnectionsHighlightIntensity;
|
||||
for (int i = 0; i < Connections.Count; i++)
|
||||
{
|
||||
Box targetBox = Connections[i];
|
||||
tmp = targetBox.PointToParent(ref center);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(ref tmp);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center);
|
||||
var highlight = 1 + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity);
|
||||
var color = _currentTypeColor * highlight;
|
||||
|
||||
// TODO: Figure out how to only draw the topmost connection
|
||||
if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, mouseOverDistance))
|
||||
{
|
||||
highlight += 0.5f;
|
||||
}
|
||||
|
||||
DrawConnection(ref startPos, ref endPos, ref color, highlight);
|
||||
}
|
||||
}
|
||||
@@ -70,12 +149,9 @@ namespace FlaxEditor.Surface.Elements
|
||||
public void DrawSelectedConnection(Box targetBox)
|
||||
{
|
||||
// Draw all the connections
|
||||
var center = Size * 0.5f;
|
||||
var tmp = PointToParent(ref center);
|
||||
var startPos = Parent.PointToParent(ref tmp);
|
||||
tmp = targetBox.PointToParent(ref center);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(ref tmp);
|
||||
DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2);
|
||||
var startPos = Parent.PointToParent(Center);
|
||||
Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center);
|
||||
DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2.5f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -125,6 +125,7 @@ namespace FlaxEditor.Surface
|
||||
AutoFocus = false;
|
||||
TooltipText = nodeArch.Description;
|
||||
CullChildren = false;
|
||||
BackgroundColor = Style.Current.BackgroundNormal;
|
||||
|
||||
if (Archetype.DefaultValues != null)
|
||||
{
|
||||
@@ -552,19 +553,22 @@ namespace FlaxEditor.Surface
|
||||
|
||||
internal Box GetNextBox(Box box)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < Elements.Count; i++)
|
||||
// Get the one after it
|
||||
for (int i = box.IndexInParent + 1; i < Elements.Count; i++)
|
||||
{
|
||||
if (Elements[i] == box)
|
||||
if (Elements[i] is Box b)
|
||||
{
|
||||
// We found the box
|
||||
break;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the one after it
|
||||
i++;
|
||||
for (; i < Elements.Count; i++)
|
||||
return null;
|
||||
}
|
||||
|
||||
internal Box GetPreviousBox(Box box)
|
||||
{
|
||||
// Get the one before it
|
||||
for (int i = box.IndexInParent - 1; i >= 0; i--)
|
||||
{
|
||||
if (Elements[i] is Box b)
|
||||
{
|
||||
@@ -754,29 +758,33 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
if (Elements[j] is OutputBox ob && ob.HasAnyConnection)
|
||||
{
|
||||
ob.DrawConnections();
|
||||
ob.DrawConnections(ref mousePosition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws all selected connections between surface objects related to this node.
|
||||
/// </summary>
|
||||
/// <param name="selectedConnectionIndex">The index of the currently selected connection.</param>
|
||||
public void DrawSelectedConnections(int selectedConnectionIndex)
|
||||
{
|
||||
if (_isSelected)
|
||||
{
|
||||
bool hasBoxesSelection = HasBoxesSelection;
|
||||
for (int j = 0; j < Elements.Count; j++)
|
||||
if (HasBoxesSelection)
|
||||
{
|
||||
if (Elements[j] is Box box && box.HasAnyConnection && (!hasBoxesSelection || box.IsSelected))
|
||||
for (int j = 0; j < Elements.Count; j++)
|
||||
{
|
||||
if (box is OutputBox ob)
|
||||
if (Elements[j] is Box box && box.IsSelected && selectedConnectionIndex < box.Connections.Count)
|
||||
{
|
||||
for (int i = 0; i < ob.Connections.Count; i++)
|
||||
if (box is OutputBox ob)
|
||||
{
|
||||
ob.DrawSelectedConnection(ob.Connections[i]);
|
||||
ob.DrawSelectedConnection(ob.Connections[selectedConnectionIndex]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < box.Connections.Count; i++)
|
||||
else
|
||||
{
|
||||
if (box.Connections[i] is OutputBox outputBox)
|
||||
if (box.Connections[selectedConnectionIndex] is OutputBox outputBox)
|
||||
{
|
||||
outputBox.DrawSelectedConnection(box);
|
||||
}
|
||||
@@ -784,6 +792,32 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < Elements.Count; j++)
|
||||
{
|
||||
if (Elements[j] is Box box)
|
||||
{
|
||||
if (box is OutputBox ob)
|
||||
{
|
||||
for (int i = 0; i < ob.Connections.Count; i++)
|
||||
{
|
||||
ob.DrawSelectedConnection(ob.Connections[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < box.Connections.Count; i++)
|
||||
{
|
||||
if (box.Connections[i] is OutputBox outputBox)
|
||||
{
|
||||
outputBox.DrawSelectedConnection(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,7 +977,7 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Background
|
||||
var backgroundRect = new Rectangle(Vector2.Zero, Size);
|
||||
Render2D.FillRectangle(backgroundRect, style.BackgroundNormal);
|
||||
Render2D.FillRectangle(backgroundRect, BackgroundColor);
|
||||
|
||||
// Breakpoint hit
|
||||
if (Breakpoint.Hit)
|
||||
@@ -1006,7 +1040,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
// Secondary Context Menu
|
||||
if (button == MouseButton.Right && false)
|
||||
if (button == MouseButton.Right)
|
||||
{
|
||||
if (!IsSelected)
|
||||
Surface.Select(this);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace FlaxEditor.Surface.Undo
|
||||
/// The helper structure for Surface node box handle.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public struct BoxHandle
|
||||
public struct BoxHandle : IEquatable<BoxHandle>
|
||||
{
|
||||
private readonly uint _nodeId;
|
||||
private readonly int _boxId;
|
||||
@@ -51,5 +51,27 @@ namespace FlaxEditor.Surface.Undo
|
||||
throw new Exception("Missing box.");
|
||||
return box;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is BoxHandle handle && Equals(handle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(BoxHandle other)
|
||||
{
|
||||
return _nodeId == other._nodeId &&
|
||||
_boxId == other._boxId;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (_nodeId.GetHashCode() * 397) ^ _boxId.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,9 +106,12 @@ namespace FlaxEditor.Surface.Undo
|
||||
}
|
||||
for (int i = 0; i < output.Length; i++)
|
||||
{
|
||||
var box = output[i].Get(context);
|
||||
oB.Connections.Add(box);
|
||||
box.Connections.Add(oB);
|
||||
if (!output[i].Equals(_input))
|
||||
{
|
||||
var box = output[i].Get(context);
|
||||
oB.Connections.Add(box);
|
||||
box.Connections.Add(oB);
|
||||
}
|
||||
}
|
||||
|
||||
toUpdate.AddRange(iB.Connections);
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
for (int j = 0; j < node.Elements.Count; j++)
|
||||
{
|
||||
if (node.Elements[j] is Box box)
|
||||
if (node.Elements[j] is Box box && box.Connections.Count > 0)
|
||||
{
|
||||
var dataModelBox = new DataModelBox
|
||||
{
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace FlaxEditor.Surface
|
||||
for (int i = 0; i < Nodes.Count; i++)
|
||||
{
|
||||
Nodes[i].DrawConnections(ref mousePosition);
|
||||
Nodes[i].DrawSelectedConnections(_selectedConnectionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -274,12 +274,47 @@ namespace FlaxEditor.Surface
|
||||
bool handled = base.OnMouseDoubleClick(location, button);
|
||||
if (!handled)
|
||||
CustomMouseDoubleClick?.Invoke(ref location, button, ref handled);
|
||||
if (handled)
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
return true;
|
||||
var mousePos = _rootControl.PointFromParent(ref _mousePos);
|
||||
if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox))
|
||||
{
|
||||
if (Undo != null)
|
||||
{
|
||||
bool undoEnabled = Undo.Enabled;
|
||||
Undo.Enabled = false;
|
||||
var rerouteNode = Context.SpawnNode(7, 29, mousePos);
|
||||
Undo.Enabled = undoEnabled;
|
||||
|
||||
var spawnNodeAction = new AddRemoveNodeAction(rerouteNode, true);
|
||||
|
||||
var disconnectBoxesAction = new ConnectBoxesAction(inputBox, outputBox, false);
|
||||
inputBox.BreakConnection(outputBox);
|
||||
disconnectBoxesAction.End();
|
||||
|
||||
var addConnectionsAction = new EditNodeConnections(Context, rerouteNode);
|
||||
outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput));
|
||||
rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox);
|
||||
addConnectionsAction.End();
|
||||
|
||||
|
||||
Undo.AddAction(new MultiUndoAction(spawnNodeAction, disconnectBoxesAction, addConnectionsAction));
|
||||
}
|
||||
else
|
||||
{
|
||||
var rerouteNode = Context.SpawnNode(7, 29, mousePos);
|
||||
inputBox.BreakConnection(outputBox);
|
||||
outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput));
|
||||
rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox);
|
||||
}
|
||||
MarkAsEdited();
|
||||
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return handled;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -527,6 +562,81 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (key == KeyboardKeys.ArrowUp || key == KeyboardKeys.ArrowDown)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
|
||||
Box toSelect = (key == KeyboardKeys.ArrowUp) ?
|
||||
selectedBox?.ParentNode.GetPreviousBox(selectedBox) :
|
||||
selectedBox?.ParentNode.GetNextBox(selectedBox);
|
||||
|
||||
if (toSelect != null && toSelect.IsOutput == selectedBox.IsOutput)
|
||||
{
|
||||
Select(toSelect.ParentNode);
|
||||
toSelect.ParentNode.SelectBox(toSelect);
|
||||
}
|
||||
}
|
||||
|
||||
if (key == KeyboardKeys.Tab)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
|
||||
int connectionCount = selectedBox.Connections.Count;
|
||||
if (connectionCount == 0) return true;
|
||||
|
||||
if (Root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
_selectedConnectionIndex = ((_selectedConnectionIndex - 1) % connectionCount + connectionCount) % connectionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedConnectionIndex = (_selectedConnectionIndex + 1) % connectionCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (key == KeyboardKeys.ArrowRight || key == KeyboardKeys.ArrowLeft)
|
||||
{
|
||||
Box selectedBox = GetSelectedBox(SelectedNodes);
|
||||
if (selectedBox == null) return true;
|
||||
|
||||
Box toSelect = null;
|
||||
|
||||
if ((key == KeyboardKeys.ArrowRight && selectedBox.IsOutput) || (key == KeyboardKeys.ArrowLeft && !selectedBox.IsOutput))
|
||||
{
|
||||
if (_selectedConnectionIndex < 0 || _selectedConnectionIndex >= selectedBox.Connections.Count)
|
||||
{
|
||||
_selectedConnectionIndex = 0;
|
||||
}
|
||||
toSelect = selectedBox.Connections[_selectedConnectionIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the node with the closest Y-level
|
||||
// Since there are cases like 3 nodes on one side and only 1 node on the other side
|
||||
|
||||
var elements = selectedBox.ParentNode.Elements;
|
||||
float minDistance = float.PositiveInfinity;
|
||||
for (int i = 0; i < elements.Count; i++)
|
||||
{
|
||||
if (elements[i] is Box box && box.IsOutput != selectedBox.IsOutput && Mathf.Abs(box.Y - selectedBox.Y) < minDistance)
|
||||
{
|
||||
toSelect = box;
|
||||
minDistance = Mathf.Abs(box.Y - selectedBox.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toSelect != null)
|
||||
{
|
||||
Select(toSelect.ParentNode);
|
||||
toSelect.ParentNode.SelectBox(toSelect);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -720,5 +830,31 @@ namespace FlaxEditor.Surface
|
||||
yLocation
|
||||
);
|
||||
}
|
||||
|
||||
private bool IntersectsConnection(Vector2 mousePosition, out InputBox inputBox, out OutputBox outputBox)
|
||||
{
|
||||
for (int i = 0; i < Nodes.Count; i++)
|
||||
{
|
||||
for (int j = 0; j < Nodes[i].Elements.Count; j++)
|
||||
{
|
||||
if (Nodes[i].Elements[j] is OutputBox ob)
|
||||
{
|
||||
for (int k = 0; k < ob.Connections.Count; k++)
|
||||
{
|
||||
if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition))
|
||||
{
|
||||
outputBox = ob;
|
||||
inputBox = ob.Connections[k] as InputBox;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputBox = null;
|
||||
inputBox = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace FlaxEditor.Surface
|
||||
private GroupArchetype _customNodesGroup;
|
||||
private List<NodeArchetype> _customNodes;
|
||||
private Action _onSave;
|
||||
private int _selectedConnectionIndex;
|
||||
|
||||
internal int _isUpdatingBoxTypes;
|
||||
|
||||
@@ -362,6 +363,8 @@ namespace FlaxEditor.Surface
|
||||
Context.ControlSpawned += OnSurfaceControlSpawned;
|
||||
Context.ControlDeleted += OnSurfaceControlDeleted;
|
||||
|
||||
SelectionChanged += () => { _selectedConnectionIndex = 0; };
|
||||
|
||||
// Init drag handlers
|
||||
DragHandlers.Add(_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem));
|
||||
DragHandlers.Add(_dragParameters = new DragNames<DragDropEventArgs>(SurfaceParameter.DragPrefix, ValidateDragParameter));
|
||||
|
||||
@@ -27,6 +27,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("Removing 'C:\\Windows\\'"),
|
||||
#elif PLATFORM_LINUX
|
||||
TEXT("Time to switch to Windows?"),
|
||||
TEXT("Installing Windows 10..."),
|
||||
#endif
|
||||
TEXT("Kappa!"),
|
||||
TEXT("How you doin'?"),
|
||||
@@ -116,6 +117,9 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("Compiling Shaders (93,788)"),
|
||||
TEXT("Hi There"),
|
||||
TEXT("BAGUETTE"),
|
||||
TEXT("All we had to do was follow the damn train, CJ"),
|
||||
TEXT("28 stab wounds"),
|
||||
TEXT("Here we go again"),
|
||||
};
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
|
||||
@@ -245,12 +245,22 @@ bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
|
||||
{
|
||||
return FindAsset(id, info);
|
||||
}
|
||||
#if !USE_EDITOR
|
||||
if (FileSystem::IsRelative(path))
|
||||
{
|
||||
// Additional check if user provides path relative to the project folder (eg. Content/SomeAssets/MyFile.json)
|
||||
const String absolutePath = Globals::ProjectFolder / *path;
|
||||
if (_pathsMapping.TryGet(absolutePath, id))
|
||||
{
|
||||
return FindAsset(id, info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Find asset in registry
|
||||
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto& e = i->Value;
|
||||
|
||||
if (e.Info.Path == path)
|
||||
{
|
||||
// Validate file exists
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
partial class BuildSettings
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
/// <summary>
|
||||
/// The build presets.
|
||||
/// </summary>
|
||||
@@ -60,5 +61,6 @@ namespace FlaxEditor.Content.Settings
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -90,41 +90,53 @@ namespace FlaxEditor.Content.Settings
|
||||
[EditorOrder(1100), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")]
|
||||
public Dictionary<string, JsonAsset> CustomSettings;
|
||||
|
||||
#if FLAX_EDITOR || PLATFORM_WINDOWS
|
||||
/// <summary>
|
||||
/// Reference to <see cref="WindowsPlatformSettings"/> asset. Used to apply configuration on Windows platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2010), EditorDisplay("Platform Settings", "Windows"), AssetReference(typeof(WindowsPlatformSettings), true), Tooltip("Reference to Windows Platform Settings asset")]
|
||||
public JsonAsset WindowsPlatform;
|
||||
#endif
|
||||
|
||||
#if FLAX_EDITOR || PLATFORM_UWP || PLATFORM_XBOX_ONE
|
||||
/// <summary>
|
||||
/// Reference to <see cref="UWPPlatformSettings"/> asset. Used to apply configuration on Universal Windows Platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2020), EditorDisplay("Platform Settings", "Universal Windows Platform"), AssetReference(typeof(UWPPlatformSettings), true), Tooltip("Reference to Universal Windows Platform Settings asset")]
|
||||
public JsonAsset UWPPlatform;
|
||||
#endif
|
||||
|
||||
#if FLAX_EDITOR || PLATFORM_LINUX
|
||||
/// <summary>
|
||||
/// Reference to <see cref="LinuxPlatformSettings"/> asset. Used to apply configuration on Linux platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2030), EditorDisplay("Platform Settings", "Linux"), AssetReference(typeof(LinuxPlatformSettings), true), Tooltip("Reference to Linux Platform Settings asset")]
|
||||
public JsonAsset LinuxPlatform;
|
||||
#endif
|
||||
|
||||
#if FLAX_EDITOR || PLATFORM_PS4
|
||||
/// <summary>
|
||||
/// Reference to PS4 Platform Settings asset. Used to apply configuration on PS4 platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2040), EditorDisplay("Platform Settings", "PlayStation 4"), AssetReference(PS4PlatformSettingsTypename, true), Tooltip("Reference to PS4 Platform Settings asset")]
|
||||
public JsonAsset PS4Platform;
|
||||
#endif
|
||||
|
||||
#if FLAX_EDITOR || PLATFORM_XBOX_SCARLETT
|
||||
/// <summary>
|
||||
/// Reference to Xbox Scarlett Platform Settings asset. Used to apply configuration on Xbox Scarlett platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2050), EditorDisplay("Platform Settings", "Xbox Scarlett"), AssetReference(XboxScarlettPlatformSettingsTypename, true), Tooltip("Reference to Xbox Scarlett Platform Settings asset")]
|
||||
public JsonAsset XboxScarlettPlatform;
|
||||
#endif
|
||||
|
||||
#if FLAX_EDITOR || PLATFORM_ANDROID
|
||||
/// <summary>
|
||||
/// Reference to <see cref="AndroidPlatformSettings"/> asset. Used to apply configuration on Android platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2060), EditorDisplay("Platform Settings", "Android"), AssetReference(typeof(AndroidPlatformSettings), true), Tooltip("Reference to Android Platform Settings asset")]
|
||||
public JsonAsset AndroidPlatform;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the game settings asset file.
|
||||
@@ -202,20 +214,32 @@ namespace FlaxEditor.Content.Settings
|
||||
return LoadAsset<BuildSettings>(gameSettings.GameCooking) as T;
|
||||
if (type == typeof(InputSettings))
|
||||
return LoadAsset<InputSettings>(gameSettings.Input) as T;
|
||||
if (type == typeof(WindowsPlatformSettings))
|
||||
return LoadAsset<WindowsPlatformSettings>(gameSettings.WindowsPlatform) as T;
|
||||
if (type == typeof(UWPPlatformSettings))
|
||||
return LoadAsset<UWPPlatformSettings>(gameSettings.UWPPlatform) as T;
|
||||
if (type == typeof(LinuxPlatformSettings))
|
||||
return LoadAsset<LinuxPlatformSettings>(gameSettings.LinuxPlatform) as T;
|
||||
if (type.FullName == PS4PlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.PS4Platform, PS4PlatformSettingsTypename) as T;
|
||||
if (type.FullName == XboxScarlettPlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.XboxScarlettPlatform, XboxScarlettPlatformSettingsTypename) as T;
|
||||
if (type == typeof(AndroidPlatformSettings))
|
||||
return LoadAsset<AndroidPlatformSettings>(gameSettings.AndroidPlatform) as T;
|
||||
if (type == typeof(AudioSettings))
|
||||
return LoadAsset<AudioSettings>(gameSettings.Audio) as T;
|
||||
#if FLAX_EDITOR || PLATFORM_WINDOWS
|
||||
if (type == typeof(WindowsPlatformSettings))
|
||||
return LoadAsset<WindowsPlatformSettings>(gameSettings.WindowsPlatform) as T;
|
||||
#endif
|
||||
#if FLAX_EDITOR || PLATFORM_UWP || PLATFORM_XBOX_ONE
|
||||
if (type == typeof(UWPPlatformSettings))
|
||||
return LoadAsset<UWPPlatformSettings>(gameSettings.UWPPlatform) as T;
|
||||
#endif
|
||||
#if FLAX_EDITOR || PLATFORM_LINUX
|
||||
if (type == typeof(LinuxPlatformSettings))
|
||||
return LoadAsset<LinuxPlatformSettings>(gameSettings.LinuxPlatform) as T;
|
||||
#endif
|
||||
#if FLAX_EDITOR || PLATFORM_PS4
|
||||
if (type.FullName == PS4PlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.PS4Platform, PS4PlatformSettingsTypename) as T;
|
||||
#endif
|
||||
#if FLAX_EDITOR || PLATFORM_XBOX_SCARLETT
|
||||
if (type.FullName == XboxScarlettPlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.XboxScarlettPlatform, XboxScarlettPlatformSettingsTypename) as T;
|
||||
#endif
|
||||
#if FLAX_EDITOR || PLATFORM_ANDROID
|
||||
if (type == typeof(AndroidPlatformSettings))
|
||||
return LoadAsset<AndroidPlatformSettings>(gameSettings.AndroidPlatform) as T;
|
||||
#endif
|
||||
|
||||
if (gameSettings.CustomSettings != null)
|
||||
{
|
||||
@@ -233,6 +257,7 @@ namespace FlaxEditor.Content.Settings
|
||||
return null;
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
private static bool SaveAsset<T>(GameSettings gameSettings, ref JsonAsset asset, T obj) where T : SettingsBase
|
||||
{
|
||||
if (asset)
|
||||
@@ -337,5 +362,6 @@ namespace FlaxEditor.Content.Settings
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern void Apply();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -269,6 +269,8 @@ public:
|
||||
{
|
||||
return String(_data.Get(), _data.Count());
|
||||
}
|
||||
|
||||
StringView ToStringView() const;
|
||||
};
|
||||
|
||||
inline uint32 GetHash(const StringBuilder& key)
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
#include "StringView.h"
|
||||
#include "String.h"
|
||||
#include "StringBuilder.h"
|
||||
|
||||
StringView StringBuilder::ToStringView() const
|
||||
{
|
||||
return StringView(_data.Get(), _data.Count());
|
||||
}
|
||||
|
||||
StringView StringView::Empty;
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include "Engine/Debug/DebugLog.h"
|
||||
#include "Engine/Render2D/Render2D.h"
|
||||
#include "Engine/Render2D/FontAsset.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#endif
|
||||
|
||||
// Debug draw service configuration
|
||||
#define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024)
|
||||
@@ -514,7 +517,11 @@ void DebugDrawService::Update()
|
||||
PROFILE_CPU();
|
||||
|
||||
// Update lists
|
||||
const float deltaTime = Time::Update.DeltaTime.GetTotalSeconds();
|
||||
float deltaTime = Time::Update.DeltaTime.GetTotalSeconds();
|
||||
#if USE_EDITOR
|
||||
if (!Editor::IsPlayMode)
|
||||
deltaTime = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
|
||||
#endif
|
||||
GlobalContext.DebugDrawDefault.Update(deltaTime);
|
||||
GlobalContext.DebugDrawDepthTest.Update(deltaTime);
|
||||
|
||||
|
||||
@@ -121,8 +121,11 @@ public:
|
||||
API_FIELD() static float PhysicsFPS;
|
||||
|
||||
/// <summary>
|
||||
/// The target amount of the frames rendered per second (actual game FPS).
|
||||
/// The target amount of the frames rendered per second (target game FPS).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To get the actual game FPS use <see cref="Engine.FramesPerSecond"/>
|
||||
/// </remarks>
|
||||
API_FIELD() static float DrawFPS;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "NavigationSettings.h"
|
||||
#include "NavMesh.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include "Engine/Core/Random.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include <ThirdParty/recastnavigation/DetourNavMesh.h>
|
||||
@@ -231,6 +231,71 @@ bool NavMeshRuntime::ProjectPoint(const Vector3& point, Vector3& result) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NavMeshRuntime::FindRandomPoint(Vector3& result) const
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
const auto query = GetNavMeshQuery();
|
||||
if (!query || !_navMesh)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dtQueryFilter filter;
|
||||
InitFilter(filter);
|
||||
|
||||
dtPolyRef randomPoly = 0;
|
||||
query->findRandomPoint(&filter, Random::Rand, &randomPoly, &result.X);
|
||||
if (!randomPoly)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Quaternion invRotation;
|
||||
Quaternion::Invert(Properties.Rotation, invRotation);
|
||||
Vector3::Transform(result, invRotation, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NavMeshRuntime::FindRandomPointAroundCircle(const Vector3& center, float radius, Vector3& result) const
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
const auto query = GetNavMeshQuery();
|
||||
if (!query || !_navMesh)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dtQueryFilter filter;
|
||||
InitFilter(filter);
|
||||
Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL);
|
||||
|
||||
Vector3 centerNavMesh;
|
||||
Vector3::Transform(center, Properties.Rotation, centerNavMesh);
|
||||
|
||||
dtPolyRef centerPoly = 0;
|
||||
query->findNearestPoly(¢erNavMesh.X, &extent.X, &filter, ¢erPoly, nullptr);
|
||||
if (!centerPoly)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dtPolyRef randomPoly = 0;
|
||||
query->findRandomPointAroundCircle(centerPoly, ¢erNavMesh.X, radius, &filter, Random::Rand, &randomPoly, &result.X);
|
||||
if (!randomPoly)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Quaternion invRotation;
|
||||
Quaternion::Invert(Properties.Rotation, invRotation);
|
||||
Vector3::Transform(result, invRotation, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NavMeshRuntime::RayCast(const Vector3& startPosition, const Vector3& endPosition, NavMeshHit& hitInfo) const
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
@@ -522,6 +587,7 @@ void NavMeshRuntime::RemoveTiles(bool (* prediction)(const NavMeshRuntime* navMe
|
||||
#if COMPILE_WITH_DEBUG_DRAW
|
||||
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
|
||||
void DrawPoly(NavMeshRuntime* navMesh, const Matrix& navMeshToWorld, const dtMeshTile& tile, const dtPoly& poly)
|
||||
{
|
||||
|
||||
@@ -127,6 +127,22 @@ public:
|
||||
/// <returns>True if found valid location on the navmesh, otherwise false.</returns>
|
||||
bool ProjectPoint(const Vector3& point, Vector3& result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Finds random location on nav mesh.
|
||||
/// </summary>
|
||||
/// <param name="result">The result position on the navmesh (valid only if method returns true).</param>
|
||||
/// <returns>True if found valid location on the navmesh, otherwise false.</returns>
|
||||
bool FindRandomPoint(Vector3& result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Finds random location on nav mesh within the reach of specified location.
|
||||
/// </summary>
|
||||
/// <param name="center">The source point to find random location around it.</param>
|
||||
/// <param name="radius">The search distance for a random point. Maximum distance for a result point from the center of the circle.</param>
|
||||
/// <param name="result">The result position on the navmesh (valid only if method returns true).</param>
|
||||
/// <returns>True if found valid location on the navmesh, otherwise false.</returns>
|
||||
bool FindRandomPointAroundCircle(const Vector3& center, float radius, Vector3& result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Casts a 'walkability' ray along the surface of the navigation mesh from the start position toward the end position.
|
||||
/// </summary>
|
||||
|
||||
@@ -315,6 +315,20 @@ bool Navigation::ProjectPoint(const Vector3& point, Vector3& result)
|
||||
return NavMeshes.First()->ProjectPoint(point, result);
|
||||
}
|
||||
|
||||
bool Navigation::FindRandomPoint(Vector3& result)
|
||||
{
|
||||
if (NavMeshes.IsEmpty())
|
||||
return false;
|
||||
return NavMeshes.First()->FindRandomPoint(result);
|
||||
}
|
||||
|
||||
bool Navigation::FindRandomPointAroundCircle(const Vector3& center, float radius, Vector3& result)
|
||||
{
|
||||
if (NavMeshes.IsEmpty())
|
||||
return false;
|
||||
return NavMeshes.First()->FindRandomPointAroundCircle(center, radius, result);
|
||||
}
|
||||
|
||||
bool Navigation::RayCast(const Vector3& startPosition, const Vector3& endPosition, NavMeshHit& hitInfo)
|
||||
{
|
||||
if (NavMeshes.IsEmpty())
|
||||
|
||||
@@ -48,6 +48,22 @@ public:
|
||||
/// <returns>True if found valid location on the navmesh, otherwise false.</returns>
|
||||
API_FUNCTION() static bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result);
|
||||
|
||||
/// <summary>
|
||||
/// Finds random location on nav mesh.
|
||||
/// </summary>
|
||||
/// <param name="result">The result position on the navmesh (valid only if method returns true).</param>
|
||||
/// <returns>True if found valid location on the navmesh, otherwise false.</returns>
|
||||
API_FUNCTION() static bool FindRandomPoint(API_PARAM(Out) Vector3& result);
|
||||
|
||||
/// <summary>
|
||||
/// Finds random location on nav mesh within the reach of specified location.
|
||||
/// </summary>
|
||||
/// <param name="center">The source point to find random location around it.</param>
|
||||
/// <param name="radius">The search distance for a random point. Maximum distance for a result point from the center of the circle.</param>
|
||||
/// <param name="result">The result position on the navmesh (valid only if method returns true).</param>
|
||||
/// <returns>True if found valid location on the navmesh, otherwise false.</returns>
|
||||
API_FUNCTION() static bool FindRandomPointAroundCircle(const Vector3& center, float radius, API_PARAM(Out) Vector3& result);
|
||||
|
||||
/// <summary>
|
||||
/// Casts a 'walkability' ray along the surface of the navigation mesh from the start position toward the end position.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,7 +9,9 @@ namespace FlaxEditor.Content.Settings
|
||||
/// <summary>
|
||||
/// The collision layers masks. Used to define layer-based collision detection.
|
||||
/// </summary>
|
||||
[EditorOrder(1040), EditorDisplay("Layers Matrix"), CustomEditor(typeof(FlaxEditor.CustomEditors.Dedicated.LayersMatrixEditor))]
|
||||
#if FLAX_EDITOR
|
||||
[EditorOrder(1040), EditorDisplay("Layers Matrix"), CustomEditor(typeof(CustomEditors.Dedicated.LayersMatrixEditor))]
|
||||
#endif
|
||||
public uint[] LayerMasks = new uint[32];
|
||||
|
||||
/// <summary>
|
||||
@@ -317,64 +317,58 @@ bool AndroidFileSystem::CopyFile(const StringView& dst, const StringView& src)
|
||||
{
|
||||
const StringAsANSI<> srcANSI(*src, src.Length());
|
||||
const StringAsANSI<> dstANSI(*dst, dst.Length());
|
||||
const char* from = srcANSI.Get();
|
||||
const char* to = dstANSI.Get();
|
||||
|
||||
int fd_to, fd_from;
|
||||
char buf[4096];
|
||||
ssize_t nread;
|
||||
int saved_errno;
|
||||
int srcFile, dstFile;
|
||||
char buffer[4096];
|
||||
ssize_t readSize;
|
||||
int cachedError;
|
||||
|
||||
fd_from = open(from, O_RDONLY);
|
||||
if (fd_from < 0)
|
||||
srcFile = open(srcANSI.Get(), O_RDONLY);
|
||||
if (srcFile < 0)
|
||||
return true;
|
||||
|
||||
fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666);
|
||||
if (fd_to < 0)
|
||||
dstFile = open(dstANSI.Get(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
if (dstFile < 0)
|
||||
goto out_error;
|
||||
|
||||
while (nread = read(fd_from, buf, sizeof buf), nread > 0)
|
||||
while (readSize = read(srcFile, buffer, sizeof(buffer)), readSize > 0)
|
||||
{
|
||||
char* out_ptr = buf;
|
||||
ssize_t nwritten;
|
||||
char* ptr = buffer;
|
||||
ssize_t writeSize;
|
||||
|
||||
do
|
||||
{
|
||||
nwritten = write(fd_to, out_ptr, nread);
|
||||
|
||||
if (nwritten >= 0)
|
||||
writeSize = write(dstFile, ptr, readSize);
|
||||
if (writeSize >= 0)
|
||||
{
|
||||
nread -= nwritten;
|
||||
out_ptr += nwritten;
|
||||
readSize -= writeSize;
|
||||
ptr += writeSize;
|
||||
}
|
||||
else if (errno != EINTR)
|
||||
{
|
||||
goto out_error;
|
||||
}
|
||||
} while (nread > 0);
|
||||
} while (readSize > 0);
|
||||
}
|
||||
|
||||
if (nread == 0)
|
||||
if (readSize == 0)
|
||||
{
|
||||
if (close(fd_to) < 0)
|
||||
if (close(dstFile) < 0)
|
||||
{
|
||||
fd_to = -1;
|
||||
dstFile = -1;
|
||||
goto out_error;
|
||||
}
|
||||
close(fd_from);
|
||||
close(srcFile);
|
||||
|
||||
// Success
|
||||
return false;
|
||||
}
|
||||
|
||||
out_error:
|
||||
saved_errno = errno;
|
||||
|
||||
close(fd_from);
|
||||
if (fd_to >= 0)
|
||||
close(fd_to);
|
||||
|
||||
errno = saved_errno;
|
||||
cachedError = errno;
|
||||
close(srcFile);
|
||||
if (dstFile >= 0)
|
||||
close(dstFile);
|
||||
errno = cachedError;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,11 +98,11 @@ void FileSystemBase::NormalizePath(String& path)
|
||||
}
|
||||
}
|
||||
|
||||
bool FileSystemBase::IsRelative(const String& path)
|
||||
bool FileSystemBase::IsRelative(const StringView& path)
|
||||
{
|
||||
const bool isRooted =
|
||||
(path.Length() >= 2 && StringUtils::IsAlpha(path[0]) && path[1] == ':') ||
|
||||
path.StartsWith(TEXT("\\\\")) ||
|
||||
path.StartsWith(StringView(TEXT("\\\\"), 2), StringSearchCase::CaseSensitive) ||
|
||||
path.StartsWith('/') ||
|
||||
path.StartsWith('\\') ||
|
||||
path.StartsWith('/');
|
||||
|
||||
@@ -104,7 +104,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="path">Input path to check</param>
|
||||
/// <returns>True if input path is relative one, otherwise false</returns>
|
||||
static bool IsRelative(const String& path);
|
||||
static bool IsRelative(const StringView& path);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves file extension (without a dot)
|
||||
|
||||
@@ -417,7 +417,7 @@ void WindowBase::OnClosed()
|
||||
RenderTask->Enabled = false;
|
||||
|
||||
// Delete object
|
||||
DeleteObject(60);
|
||||
DeleteObject(1);
|
||||
}
|
||||
|
||||
void WindowBase::OnGotFocus()
|
||||
|
||||
@@ -47,6 +47,15 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView&
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinuxFileSystem::ShowFileExplorer(const StringView& path)
|
||||
{
|
||||
const StringAsANSI<> pathAnsi(*path, path.Length());
|
||||
char cmd[2048];
|
||||
sprintf(cmd, "nautilus %s &", pathAnsi.Get());
|
||||
system(cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinuxFileSystem::CreateDirectory(const StringView& path)
|
||||
{
|
||||
const StringAsANSI<> pathAnsi(*path, path.Length());
|
||||
@@ -309,64 +318,58 @@ bool LinuxFileSystem::CopyFile(const StringView& dst, const StringView& src)
|
||||
{
|
||||
const StringAsANSI<> srcANSI(*src, src.Length());
|
||||
const StringAsANSI<> dstANSI(*dst, dst.Length());
|
||||
const char* from = srcANSI.Get();
|
||||
const char* to = dstANSI.Get();
|
||||
|
||||
int fd_to, fd_from;
|
||||
char buf[4096];
|
||||
ssize_t nread;
|
||||
int saved_errno;
|
||||
int srcFile, dstFile;
|
||||
char buffer[4096];
|
||||
ssize_t readSize;
|
||||
int cachedError;
|
||||
|
||||
fd_from = open(from, O_RDONLY);
|
||||
if (fd_from < 0)
|
||||
srcFile = open(srcANSI.Get(), O_RDONLY);
|
||||
if (srcFile < 0)
|
||||
return true;
|
||||
|
||||
fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666);
|
||||
if (fd_to < 0)
|
||||
dstFile = open(dstANSI.Get(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
if (dstFile < 0)
|
||||
goto out_error;
|
||||
|
||||
while (nread = read(fd_from, buf, sizeof buf), nread > 0)
|
||||
while (readSize = read(srcFile, buffer, sizeof(buffer)), readSize > 0)
|
||||
{
|
||||
char* out_ptr = buf;
|
||||
ssize_t nwritten;
|
||||
char* ptr = buffer;
|
||||
ssize_t writeSize;
|
||||
|
||||
do
|
||||
{
|
||||
nwritten = write(fd_to, out_ptr, nread);
|
||||
|
||||
if (nwritten >= 0)
|
||||
writeSize = write(dstFile, ptr, readSize);
|
||||
if (writeSize >= 0)
|
||||
{
|
||||
nread -= nwritten;
|
||||
out_ptr += nwritten;
|
||||
readSize -= writeSize;
|
||||
ptr += writeSize;
|
||||
}
|
||||
else if (errno != EINTR)
|
||||
{
|
||||
goto out_error;
|
||||
}
|
||||
} while (nread > 0);
|
||||
} while (readSize > 0);
|
||||
}
|
||||
|
||||
if (nread == 0)
|
||||
if (readSize == 0)
|
||||
{
|
||||
if (close(fd_to) < 0)
|
||||
if (close(dstFile) < 0)
|
||||
{
|
||||
fd_to = -1;
|
||||
dstFile = -1;
|
||||
goto out_error;
|
||||
}
|
||||
close(fd_from);
|
||||
close(srcFile);
|
||||
|
||||
// Success
|
||||
return false;
|
||||
}
|
||||
|
||||
out_error:
|
||||
saved_errno = errno;
|
||||
|
||||
close(fd_from);
|
||||
if (fd_to >= 0)
|
||||
close(fd_to);
|
||||
|
||||
errno = saved_errno;
|
||||
cachedError = errno;
|
||||
close(srcFile);
|
||||
if (dstFile >= 0)
|
||||
close(dstFile);
|
||||
errno = cachedError;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
|
||||
// [FileSystemBase]
|
||||
static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames);
|
||||
static bool ShowFileExplorer(const StringView& path);
|
||||
static bool CreateDirectory(const StringView& path);
|
||||
static bool DeleteDirectory(const String& path, bool deleteContents = true);
|
||||
static bool DirectoryExists(const StringView& path);
|
||||
|
||||
@@ -808,6 +808,8 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
|
||||
|
||||
int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
{
|
||||
if (event->error_code == 5)
|
||||
return 0; // BadAtom (invalid Atom parameter)
|
||||
char buffer[256];
|
||||
XGetErrorText(display, event->error_code, buffer, sizeof(buffer));
|
||||
LOG(Error, "X11 Error: {0}", String(buffer));
|
||||
@@ -2055,12 +2057,15 @@ bool LinuxPlatform::GetHasFocus()
|
||||
|
||||
bool LinuxPlatform::CanOpenUrl(const StringView& url)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinuxPlatform::OpenUrl(const StringView& url)
|
||||
{
|
||||
// TODO: add support for OpenUrl on Linux
|
||||
const StringAsANSI<> urlAnsi(*url, url.Length());
|
||||
char cmd[2048];
|
||||
sprintf(cmd, "xdg-open %s", urlAnsi.Get());
|
||||
system(cmd);
|
||||
}
|
||||
|
||||
Vector2 LinuxPlatform::GetMousePosition()
|
||||
|
||||
@@ -50,11 +50,32 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
: WindowBase(settings)
|
||||
{
|
||||
// Cache data
|
||||
int32 x = Math::TruncToInt(settings.Position.X);
|
||||
int32 y = Math::TruncToInt(settings.Position.Y);
|
||||
int32 width = Math::TruncToInt(settings.Size.X);
|
||||
int32 height = Math::TruncToInt(settings.Size.Y);
|
||||
_clientSize = Vector2((float)width, (float)height);
|
||||
int32 x = 0, y = 0;
|
||||
switch (settings.StartPosition)
|
||||
{
|
||||
case WindowStartPosition::CenterParent:
|
||||
if (settings.Parent)
|
||||
{
|
||||
Rectangle parentBounds = settings.Parent->GetClientBounds();
|
||||
x = Math::TruncToInt(parentBounds.Location.X + (parentBounds.Size.X - _clientSize.X) * 0.5f);
|
||||
y = Math::TruncToInt(parentBounds.Location.Y + (parentBounds.Size.Y - _clientSize.Y) * 0.5f);
|
||||
}
|
||||
break;
|
||||
case WindowStartPosition::CenterScreen:
|
||||
{
|
||||
Vector2 desktopSize = Platform::GetDesktopSize();
|
||||
x = Math::TruncToInt((desktopSize.X - _clientSize.X) * 0.5f);
|
||||
y = Math::TruncToInt((desktopSize.Y - _clientSize.Y) * 0.5f);
|
||||
}
|
||||
break;
|
||||
case WindowStartPosition::Manual:
|
||||
x = Math::TruncToInt(settings.Position.X);
|
||||
y = Math::TruncToInt(settings.Position.Y);
|
||||
break;
|
||||
}
|
||||
_resizeDisabled = !settings.HasSizingFrame;
|
||||
|
||||
auto display = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||
@@ -78,25 +99,12 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
|
||||
// TODO: implement all window settings
|
||||
/*
|
||||
WindowStartPosition StartPosition;
|
||||
bool Fullscreen;
|
||||
bool AllowInput;
|
||||
bool AllowMinimize;
|
||||
bool AllowMaximize;
|
||||
bool AllowDragAndDrop;
|
||||
*/
|
||||
|
||||
switch (settings.StartPosition)
|
||||
{
|
||||
case WindowStartPosition::CenterParent:
|
||||
break;
|
||||
case WindowStartPosition::CenterScreen:
|
||||
break;
|
||||
case WindowStartPosition::Manual:
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
|
||||
const X11::Window window = X11::XCreateWindow(
|
||||
display, X11::XRootWindow(display, screen), x, y,
|
||||
width, height, 0, visualInfo->depth, InputOutput,
|
||||
@@ -161,17 +169,19 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
// Adjust style for borderless windows
|
||||
if (!settings.HasBorder)
|
||||
{
|
||||
// [Reference: https://www.tonyobryan.com//index.php?article=9]
|
||||
typedef struct X11Hints
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
unsigned long functions = 0;
|
||||
unsigned long decorations = 0;
|
||||
long inputMode = 0;
|
||||
unsigned long status = 0;
|
||||
unsigned long flags;
|
||||
unsigned long functions;
|
||||
unsigned long decorations;
|
||||
long inputMode;
|
||||
unsigned long status;
|
||||
} X11Hints;
|
||||
X11Hints hints;
|
||||
hints.flags = 2;
|
||||
X11::Atom wmHints = X11::XInternAtom(display, "_MOTIF_WM_HINTS", 1);
|
||||
hints.decorations = 0;
|
||||
X11::Atom wmHints = X11::XInternAtom(display, "_MOTIF_WM_HINTS", 0);
|
||||
X11::Atom property;
|
||||
if (wmHints)
|
||||
X11::XChangeProperty(display, window, property, property, 32, PropModeReplace, (unsigned char*)&hints, 5);
|
||||
@@ -180,13 +190,13 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
// Adjust type for utility windows
|
||||
if (!settings.IsRegularWindow)
|
||||
{
|
||||
X11::Atom wmTypeUtility = X11::XInternAtom(display, "_NET_WM_WINDOW_TYPE_UTILITY", 0);
|
||||
X11::Atom wmType = X11::XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0);
|
||||
if (wmTypeUtility && wmType)
|
||||
X11::XChangeProperty(display, window, wmTypeUtility, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)&wmType, 1);
|
||||
X11::Atom value = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", 0);
|
||||
X11::Atom wmType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0);
|
||||
if (value && wmType)
|
||||
X11::XChangeProperty(display, window, wmType, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)&value, 1);
|
||||
}
|
||||
|
||||
// Initialize statee
|
||||
// Initialize state
|
||||
X11::Atom wmState = X11::XInternAtom(display, "_NET_WM_STATE", 0);
|
||||
X11::Atom wmStateAbove = X11::XInternAtom(display, "_NET_WM_STATE_ABOVE", 0);
|
||||
X11::Atom wmSateSkipTaskbar = X11::XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", 0);
|
||||
@@ -231,6 +241,7 @@ void LinuxWindow::Show()
|
||||
{
|
||||
if (!_visible)
|
||||
{
|
||||
InitSwapChain();
|
||||
if (_showAfterFirstPaint)
|
||||
{
|
||||
if (RenderTask)
|
||||
|
||||
@@ -209,6 +209,7 @@ namespace
|
||||
Matrix3x3 TransformCached;
|
||||
|
||||
Array<ClipMask, InlinedAllocation<64>> ClipLayersStack;
|
||||
Array<Color, InlinedAllocation<64>> TintLayersStack;
|
||||
|
||||
// Shader
|
||||
AssetReference<Shader> GUIShader;
|
||||
@@ -252,9 +253,9 @@ FORCE_INLINE Render2DVertex MakeVertex(const Vector2& pos, const Vector2& uv, co
|
||||
{
|
||||
point,
|
||||
Half2(uv),
|
||||
color,
|
||||
color * TintLayersStack.Peek(),
|
||||
{ 0.0f, (float)Render2D::Features },
|
||||
ClipLayersStack.Peek().Mask,
|
||||
ClipLayersStack.Peek().Mask
|
||||
};
|
||||
}
|
||||
|
||||
@@ -270,6 +271,18 @@ FORCE_INLINE Render2DVertex MakeVertex(const Vector2& point, const Vector2& uv,
|
||||
};
|
||||
}
|
||||
|
||||
FORCE_INLINE Render2DVertex MakeVertex(const Vector2& point, const Vector2& uv, const Color& color, const RotatedRectangle& mask, const Vector2& customData, const Color& tint)
|
||||
{
|
||||
return
|
||||
{
|
||||
point,
|
||||
Half2(uv),
|
||||
color * tint,
|
||||
customData,
|
||||
mask
|
||||
};
|
||||
}
|
||||
|
||||
void WriteTri(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& uv0, const Vector2& uv1, const Vector2& uv2, const Color& color0, const Color& color1, const Color& color2)
|
||||
{
|
||||
Render2DVertex tris[3];
|
||||
@@ -283,7 +296,7 @@ void WriteTri(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vec
|
||||
indices[1] = VBIndex + 1;
|
||||
indices[2] = VBIndex + 2;
|
||||
IB.Write(indices, sizeof(indices));
|
||||
|
||||
|
||||
VBIndex += 3;
|
||||
IBIndex += 3;
|
||||
}
|
||||
@@ -548,6 +561,7 @@ bool Render2DService::Init()
|
||||
|
||||
void Render2DService::Dispose()
|
||||
{
|
||||
TintLayersStack.Resize(0);
|
||||
ClipLayersStack.Resize(0);
|
||||
DrawCalls.Resize(0);
|
||||
Lines.Resize(0);
|
||||
@@ -620,6 +634,10 @@ void Render2D::Begin(GPUContext* context, GPUTextureView* output, GPUTextureView
|
||||
ClipLayersStack.Clear();
|
||||
ClipLayersStack.Add({ defaultMask, defaultBounds });
|
||||
|
||||
// Initialize default tint stack
|
||||
TintLayersStack.Clear();
|
||||
TintLayersStack.Add({ 1, 1, 1, 1 });
|
||||
|
||||
// Scissors can be enabled only for 2D orthographic projections
|
||||
IsScissorsRectEnabled = false;
|
||||
|
||||
@@ -795,6 +813,25 @@ void Render2D::PopClip()
|
||||
OnClipScissors();
|
||||
}
|
||||
|
||||
void Render2D::PushTint(const Color& tint, bool inherit)
|
||||
{
|
||||
RENDER2D_CHECK_RENDERING_STATE;
|
||||
|
||||
TintLayersStack.Push(inherit ? tint * TintLayersStack.Peek() : tint);
|
||||
}
|
||||
|
||||
void Render2D::PeekTint(Color& tint)
|
||||
{
|
||||
tint = TintLayersStack.Peek();
|
||||
}
|
||||
|
||||
void Render2D::PopTint()
|
||||
{
|
||||
RENDER2D_CHECK_RENDERING_STATE;
|
||||
|
||||
TintLayersStack.Pop();
|
||||
}
|
||||
|
||||
void CalculateKernelSize(float strength, int32& kernelSize, int32& downSample)
|
||||
{
|
||||
kernelSize = Math::RoundToInt(strength * 3.0f);
|
||||
@@ -1263,12 +1300,32 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex
|
||||
DrawText(font, StringView(text.Get() + textRange.StartIndex, textRange.Length()), color, layout, customMaterial);
|
||||
}
|
||||
|
||||
FORCE_INLINE bool NeedAlphaWithTint(const Color& color)
|
||||
{
|
||||
return (color.A * TintLayersStack.Peek().A) < 1.0f;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2)
|
||||
{
|
||||
return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2, const Color& color3)
|
||||
{
|
||||
return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f || (color3.A * TintLayersStack.Peek().A) < 1.0f;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2, const Color& color3, const Color& color4)
|
||||
{
|
||||
return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f || (color3.A * TintLayersStack.Peek().A) < 1.0f || (color4.A * TintLayersStack.Peek().A) < 1.0f;
|
||||
}
|
||||
|
||||
void Render2D::FillRectangle(const Rectangle& rect, const Color& color)
|
||||
{
|
||||
RENDER2D_CHECK_RENDERING_STATE;
|
||||
|
||||
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||
drawCall.Type = color.A < 1.0f ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.Type = NeedAlphaWithTint(color) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.StartIB = IBIndex;
|
||||
drawCall.CountIB = 6;
|
||||
WriteRect(rect, color);
|
||||
@@ -1279,7 +1336,7 @@ void Render2D::FillRectangle(const Rectangle& rect, const Color& color1, const C
|
||||
RENDER2D_CHECK_RENDERING_STATE;
|
||||
|
||||
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||
drawCall.Type = color1.A < 1.0f || color2.A < 1.0f || color3.A < 1.0f || color4.A < 1.0f ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.Type = NeedAlphaWithTint(color1, color2, color3, color4) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.StartIB = IBIndex;
|
||||
drawCall.CountIB = 6;
|
||||
WriteRect(rect, color1, color2, color3, color4);
|
||||
@@ -1374,7 +1431,7 @@ void Render2D::DrawRectangle(const Rectangle& rect, const Color& color1, const C
|
||||
}
|
||||
#else
|
||||
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||
drawCall.Type = color1.A < 1.0f || color2.A < 1.0f ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.Type = NeedAlphaWithTint(color1, color2) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.StartIB = IBIndex;
|
||||
drawCall.CountIB = 4 * (6 + 3);
|
||||
|
||||
@@ -1638,7 +1695,7 @@ void DrawLines(const Vector2* points, int32 pointsCount, const Color& color1, co
|
||||
#else
|
||||
const float thicknessHalf = thickness * 0.5f;
|
||||
|
||||
drawCall.Type = color1.A < 1.0f || color2.A < 1.0f ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.Type = NeedAlphaWithTint(color1, color2) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.CountIB = 0;
|
||||
|
||||
ApplyTransform(points[0], p1t);
|
||||
@@ -1748,9 +1805,9 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength)
|
||||
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Vector2>& vertices, const Span<Vector2>& uvs)
|
||||
{
|
||||
CHECK(vertices.Length() == uvs.Length())
|
||||
|
||||
|
||||
RENDER2D_CHECK_RENDERING_STATE;
|
||||
|
||||
|
||||
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||
drawCall.Type = DrawCallType::FillTexture;
|
||||
drawCall.StartIB = IBIndex;
|
||||
@@ -1764,9 +1821,9 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Vector2>& vertice
|
||||
void Render2D::FillTriangles(const Span<Vector2>& vertices, const Span<Color>& colors, bool useAlpha)
|
||||
{
|
||||
CHECK(vertices.Length() == colors.Length());
|
||||
|
||||
|
||||
RENDER2D_CHECK_RENDERING_STATE;
|
||||
|
||||
|
||||
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||
drawCall.Type = useAlpha ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.StartIB = IBIndex;
|
||||
@@ -1779,9 +1836,9 @@ void Render2D::FillTriangles(const Span<Vector2>& vertices, const Span<Color>& c
|
||||
void Render2D::FillTriangle(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Color& color)
|
||||
{
|
||||
RENDER2D_CHECK_RENDERING_STATE;
|
||||
|
||||
|
||||
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||
drawCall.Type = color.A < 1.0f ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.Type = NeedAlphaWithTint(color) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||
drawCall.StartIB = IBIndex;
|
||||
drawCall.CountIB = 3;
|
||||
WriteTri(p0, p1, p2, color, color, color);
|
||||
|
||||
@@ -153,6 +153,24 @@ public:
|
||||
/// </summary>
|
||||
API_FUNCTION() static void PopClip();
|
||||
|
||||
/// <summary>
|
||||
/// Pushes tint color.
|
||||
/// </summary>
|
||||
/// <param name="tint">The tint color.</param>
|
||||
/// <param name="inherit">Multiply <paramref ref="tint"/> by the last tint on the stack.</param>
|
||||
API_FUNCTION() static void PushTint(API_PARAM(Ref) const Color& tint, bool inherit = true);
|
||||
|
||||
/// <summary>
|
||||
/// Peeks the current tint color.
|
||||
/// </summary>
|
||||
/// <param name="tint">The output tint color.</param>
|
||||
API_FUNCTION() static void PeekTint(API_PARAM(Out) Color& tint);
|
||||
|
||||
/// <summary>
|
||||
/// Pops tint color.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void PopTint();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
@@ -348,7 +366,7 @@ public:
|
||||
/// <param name="vertices">The vertices array.</param>
|
||||
/// <param name="uvs">The uvs array.</param>
|
||||
API_FUNCTION() static void DrawTexturedTriangles(GPUTexture* t, const Span<Vector2>& vertices, const Span<Vector2>& uvs);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws vertices array.
|
||||
/// </summary>
|
||||
|
||||
@@ -118,6 +118,8 @@ void* MonoCalloc(size_t count, size_t size)
|
||||
|
||||
#if USE_MONO_PROFILER
|
||||
|
||||
#include "Engine/Core/Types/StringBuilder.h"
|
||||
|
||||
struct FlaxMonoProfiler
|
||||
{
|
||||
};
|
||||
@@ -126,7 +128,7 @@ FlaxMonoProfiler Profiler;
|
||||
|
||||
struct StackWalkDataResult
|
||||
{
|
||||
StringAnsi Buffer;
|
||||
StringBuilder Buffer;
|
||||
};
|
||||
|
||||
mono_bool OnStackWalk(MonoMethod* method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
|
||||
@@ -138,16 +140,16 @@ mono_bool OnStackWalk(MonoMethod* method, int32_t native_offset, int32_t il_offs
|
||||
auto mName = mono_method_get_name(method);
|
||||
auto mKlassNameSpace = mono_class_get_namespace(mono_method_get_class(method));
|
||||
auto mKlassName = mono_class_get_name(mono_method_get_class(method));
|
||||
result->Buffer += mKlassNameSpace;
|
||||
result->Buffer += ".";
|
||||
result->Buffer += mKlassName;
|
||||
result->Buffer += "::";
|
||||
result->Buffer += mName;
|
||||
result->Buffer += "\n";
|
||||
result->Buffer.Append(mKlassNameSpace);
|
||||
result->Buffer.Append(TEXT("."));
|
||||
result->Buffer.Append(mKlassName);
|
||||
result->Buffer.Append(TEXT("::"));
|
||||
result->Buffer.Append(mName);
|
||||
result->Buffer.Append(TEXT("\n"));
|
||||
}
|
||||
else if (!managed)
|
||||
{
|
||||
result->Buffer += "<unmanaged>\n";
|
||||
result->Buffer.Append(TEXT("<unmanaged>\n"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -170,10 +172,10 @@ void OnGCAllocation(MonoProfiler* profiler, MonoObject* obj)
|
||||
if (details)
|
||||
{
|
||||
StackWalkDataResult stackTrace;
|
||||
stackTrace.Buffer.reserve(1024);
|
||||
stackTrace.Buffer.SetCapacity(1024);
|
||||
mono_stack_walk(&OnStackWalk, &stackTrace);
|
||||
|
||||
LOG(Info, "GC new: {0}.{1} ({2} bytes). Stack Trace:\n{3}", name_space, name, size, stackTrace.Buffer.c_str());
|
||||
LOG(Info, "GC new: {0}.{1} ({2} bytes). Stack Trace:\n{3}", String(name_space), String(name), size, stackTrace.Buffer.ToStringView());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -77,14 +77,12 @@ void Script::SetParent(Actor* value, bool canBreakPrefabLink)
|
||||
// Check if value won't change
|
||||
if (_parent == value)
|
||||
return;
|
||||
if (!IsInMainThread())
|
||||
if (IsDuringPlay() && !IsInMainThread())
|
||||
{
|
||||
LOG(Error, "Editing scene hierarchy is only allowed on a main thread.");
|
||||
return;
|
||||
}
|
||||
|
||||
Level::ScenesLock.Lock();
|
||||
|
||||
// Unlink from the old one
|
||||
if (_parent)
|
||||
{
|
||||
@@ -107,8 +105,6 @@ void Script::SetParent(Actor* value, bool canBreakPrefabLink)
|
||||
_parent->Scripts.Add(this);
|
||||
}
|
||||
|
||||
Level::ScenesLock.Unlock();
|
||||
|
||||
// Break prefab link for prefab instance objects
|
||||
if (HasPrefabLink() && IsDuringPlay() && canBreakPrefabLink)
|
||||
{
|
||||
@@ -137,7 +133,7 @@ void Script::SetParent(Actor* value, bool canBreakPrefabLink)
|
||||
Enable();
|
||||
}
|
||||
}
|
||||
else if (!previous && value->IsDuringPlay() && value->IsActiveInHierarchy() && GetEnabled())
|
||||
else if (!previous && value && value->IsDuringPlay() && value->IsActiveInHierarchy() && GetEnabled())
|
||||
{
|
||||
// Call enable when script is added to actor (previous actor was null)
|
||||
Enable();
|
||||
|
||||
@@ -569,11 +569,9 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
|
||||
{
|
||||
auto& tmpImg = GET_TMP_IMG();
|
||||
|
||||
LOG(Info, "Resizing texture from {0}x{1} to {2}x{3}.", sourceWidth, sourceHeight, width, height);
|
||||
|
||||
// During resizing we need to keep texture aspect ratio
|
||||
const bool keepAspectRation = false; // TODO: expose as import option
|
||||
if (keepAspectRation)
|
||||
const bool keepAspectRatio = false; // TODO: expose as import option
|
||||
if (keepAspectRatio)
|
||||
{
|
||||
const float aspectRatio = static_cast<float>(sourceWidth) / sourceHeight;
|
||||
if (width >= height)
|
||||
@@ -583,6 +581,7 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
|
||||
}
|
||||
|
||||
// Resize source texture
|
||||
LOG(Info, "Resizing texture from {0}x{1} to {2}x{3}.", sourceWidth, sourceHeight, width, height);
|
||||
result = DirectX::Resize(*currentImage->GetImages(), width, height, DirectX::TEX_FILTER_LINEAR, tmpImg);
|
||||
if (FAILED(result))
|
||||
{
|
||||
|
||||
@@ -354,6 +354,8 @@ bool TextureTool::Resize(TextureData& dst, const TextureData& src, int32 dstWidt
|
||||
|
||||
#if COMPILE_WITH_DIRECTXTEX
|
||||
return ResizeDirectXTex(dst, src, dstWidth, dstHeight);
|
||||
#elif COMPILE_WITH_STB
|
||||
return ResizeStb(dst, src, dstWidth, dstHeight);
|
||||
#else
|
||||
LOG(Warning, "Resizing textures is not supported on this platform.");
|
||||
return true;
|
||||
|
||||
@@ -291,6 +291,7 @@ private:
|
||||
#if COMPILE_WITH_STB
|
||||
static bool ExportTextureStb(ImageType type, const StringView& path, const TextureData& textureData);
|
||||
static bool ImportTextureStb(ImageType type, const StringView& path, TextureData& textureData, bool& hasAlpha);
|
||||
static bool ResizeStb(TextureData& dst, const TextureData& src, int32 dstWidth, int32 dstHeight);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
#define STBI_REALLOC(p, newsz) AllocatorExt::Realloc(p, newsz)
|
||||
#define STBI_REALLOC_SIZED(p, oldsz, newsz) AllocatorExt::Realloc(p, oldsz, newsz)
|
||||
#define STBI_FREE(p) Allocator::Free(p)
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <ThirdParty/stb/stb_image_write.h>
|
||||
|
||||
#define STBI_NO_PSD
|
||||
#define STBI_NO_PIC
|
||||
#define STBI_NO_PNM
|
||||
@@ -27,6 +23,20 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <ThirdParty/stb/stb_image.h>
|
||||
|
||||
#define STBIW_ASSERT(x) ASSERT(x)
|
||||
#define STBIW_MALLOC(sz) Allocator::Allocate(sz)
|
||||
#define STBIW_REALLOC(p, newsz) AllocatorExt::Realloc(p, newsz)
|
||||
#define STBIW_REALLOC_SIZED(p, oldsz, newsz) AllocatorExt::Realloc(p, oldsz, newsz)
|
||||
#define STBIW_FREE(p) Allocator::Free(p)
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <ThirdParty/stb/stb_image_write.h>
|
||||
|
||||
#define STBIR_ASSERT(x) ASSERT(x)
|
||||
#define STBIR_MALLOC(sz, c) Allocator::Allocate(sz)
|
||||
#define STBIR_FREE(p, c) Allocator::Free(p)
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#include <ThirdParty/stb/stb_image_resize.h>
|
||||
|
||||
static void stbWrite(void* context, void* data, int size)
|
||||
{
|
||||
auto file = (FileWriteStream*)context;
|
||||
@@ -171,8 +181,8 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu
|
||||
case ImageType::HDR:
|
||||
case ImageType::TGA:
|
||||
{
|
||||
int x, y, comp;
|
||||
stbi_uc* stbData = stbi_load_from_memory(fileData.Get(), fileData.Count(), &x, &y, &comp, 4);
|
||||
int width, height, components;
|
||||
stbi_uc* stbData = stbi_load_from_memory(fileData.Get(), fileData.Count(), &width, &height, &components, 4);
|
||||
if (!stbData)
|
||||
{
|
||||
LOG(Warning, "Failed to load image.");
|
||||
@@ -180,20 +190,29 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu
|
||||
}
|
||||
|
||||
// Setup texture data
|
||||
textureData.Width = x;
|
||||
textureData.Height = y;
|
||||
textureData.Width = width;
|
||||
textureData.Height = height;
|
||||
textureData.Depth = 1;
|
||||
textureData.Format = PixelFormat::R8G8B8A8_UNorm;
|
||||
textureData.Items.Resize(1);
|
||||
textureData.Items[0].Mips.Resize(1);
|
||||
auto& mip = textureData.Items[0].Mips[0];
|
||||
mip.RowPitch = sizeof(Color32) * x;
|
||||
mip.DepthPitch = mip.RowPitch * y;
|
||||
mip.Lines = y;
|
||||
mip.RowPitch = sizeof(Color32) * width;
|
||||
mip.DepthPitch = mip.RowPitch * height;
|
||||
mip.Lines = height;
|
||||
mip.Data.Copy(stbData, mip.DepthPitch);
|
||||
|
||||
#if USE_EDITOR
|
||||
// TODO: detect hasAlpha
|
||||
// Detect alpha channel usage
|
||||
auto ptrAlpha = (Color32*)mip.Data.Get();
|
||||
for (int32 y = 0; y < height && !hasAlpha; y++)
|
||||
{
|
||||
for (int32 x = 0; x < width && !hasAlpha; x++)
|
||||
{
|
||||
hasAlpha |= ptrAlpha->A < 255;
|
||||
ptrAlpha++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
stbi_image_free(stbData);
|
||||
@@ -241,4 +260,100 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextureTool::ResizeStb(TextureData& dst, const TextureData& src, int32 dstWidth, int32 dstHeight)
|
||||
{
|
||||
// Setup
|
||||
auto arraySize = src.GetArraySize();
|
||||
dst.Width = dstWidth;
|
||||
dst.Height = dstHeight;
|
||||
dst.Depth = src.Depth;
|
||||
dst.Format = src.Format;
|
||||
dst.Items.Resize(arraySize);
|
||||
auto formatSize = PixelFormatExtensions::SizeInBytes(src.Format);
|
||||
auto components = PixelFormatExtensions::ComputeComponentsCount(src.Format);
|
||||
|
||||
// Resize all array slices
|
||||
for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
|
||||
{
|
||||
const auto& srcSlice = src.Items[arrayIndex];
|
||||
auto& dstSlice = dst.Items[arrayIndex];
|
||||
auto mipLevels = srcSlice.Mips.Count();
|
||||
dstSlice.Mips.Resize(mipLevels);
|
||||
|
||||
// Resize all mip levels
|
||||
for (int32 mipIndex = 0; mipIndex < mipLevels; mipIndex++)
|
||||
{
|
||||
const auto& srcMip = srcSlice.Mips[mipIndex];
|
||||
auto& dstMip = dstSlice.Mips[mipIndex];
|
||||
auto srcMipWidth = srcMip.RowPitch / formatSize;
|
||||
auto srcMipHeight = srcMip.DepthPitch / srcMip.RowPitch;
|
||||
auto dstMipWidth = Math::Max(dstWidth << mipIndex, 1);
|
||||
auto dstMipHeight = Math::Max(dstHeight << mipIndex, 1);
|
||||
|
||||
// Allocate memory
|
||||
dstMip.RowPitch = dstMipWidth * formatSize;
|
||||
dstMip.DepthPitch = dstMip.RowPitch * dstMipHeight;
|
||||
dstMip.Lines = dstMipHeight;
|
||||
dstMip.Data.Allocate(dstMip.DepthPitch);
|
||||
|
||||
// Resize texture
|
||||
switch (src.Format)
|
||||
{
|
||||
case PixelFormat::R8_Typeless:
|
||||
case PixelFormat::R8_SInt:
|
||||
case PixelFormat::R8_SNorm:
|
||||
case PixelFormat::R8G8_Typeless:
|
||||
case PixelFormat::R8G8_SInt:
|
||||
case PixelFormat::R8G8_SNorm:
|
||||
case PixelFormat::R8G8B8A8_Typeless:
|
||||
case PixelFormat::R8G8B8A8_UNorm:
|
||||
case PixelFormat::R8G8B8A8_UInt:
|
||||
case PixelFormat::R8G8B8A8_SNorm:
|
||||
case PixelFormat::R8G8B8A8_SInt:
|
||||
case PixelFormat::B8G8R8A8_UNorm:
|
||||
case PixelFormat::B8G8R8X8_Typeless:
|
||||
case PixelFormat::B8G8R8X8_UNorm:
|
||||
{
|
||||
if (!stbir_resize_uint8((const uint8*)srcMip.Data.Get(), srcMipWidth, srcMipHeight, srcMip.RowPitch, (uint8*)dstMip.Data.Get(), dstMipWidth, dstMipHeight, dstMip.RowPitch, components))
|
||||
{
|
||||
LOG(Warning, "Cannot resize image.");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PixelFormat::R8G8B8A8_UNorm_sRGB:
|
||||
case PixelFormat::B8G8R8A8_UNorm_sRGB:
|
||||
case PixelFormat::B8G8R8X8_UNorm_sRGB:
|
||||
{
|
||||
auto alphaChannel = src.Format == PixelFormat::B8G8R8X8_UNorm_sRGB ? STBIR_ALPHA_CHANNEL_NONE : 3;
|
||||
if (!stbir_resize_uint8_srgb((const uint8*)srcMip.Data.Get(), srcMipWidth, srcMipHeight, srcMip.RowPitch, (uint8*)dstMip.Data.Get(), dstMipWidth, dstMipHeight, dstMip.RowPitch, components, alphaChannel, 0))
|
||||
{
|
||||
LOG(Warning, "Cannot resize image.");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PixelFormat::R32_Typeless:
|
||||
case PixelFormat::R32_Float:
|
||||
case PixelFormat::R32G32_Float:
|
||||
case PixelFormat::R32G32B32_Float:
|
||||
case PixelFormat::R32G32B32A32_Float:
|
||||
{
|
||||
if (!stbir_resize_float((const float*)srcMip.Data.Get(), srcMipWidth, srcMipHeight, srcMip.RowPitch, (float*)dstMip.Data.Get(), dstMipWidth, dstMipHeight, dstMip.RowPitch, components))
|
||||
{
|
||||
LOG(Warning, "Cannot resize image.");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG(Warning, "Cannot resize image. Unsupported format {0}", static_cast<int32>(src.Format));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -770,6 +770,11 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
#undef PLATFORM_CASE
|
||||
break;
|
||||
}
|
||||
case 29:
|
||||
{
|
||||
value = tryGetValue(node->GetBox(0), Value::Zero);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -738,6 +738,12 @@ void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
value = Scripting::FindObject<Actor>((Guid)node->Values[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 29:
|
||||
{
|
||||
value = tryGetValue(node->GetBox(0), Value::Zero);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -183,6 +183,13 @@ float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, floa
|
||||
return dx*dx + dz*dz;
|
||||
}
|
||||
|
||||
float dtDistancePtPtSqr2D(const float* pt, const float* p)
|
||||
{
|
||||
float dx = pt[0] - p[0];
|
||||
float dz = pt[2] - p[2];
|
||||
return dx*dx + dz*dz;
|
||||
}
|
||||
|
||||
void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts)
|
||||
{
|
||||
tc[0] = 0.0f;
|
||||
@@ -203,14 +210,18 @@ void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const floa
|
||||
|
||||
bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
|
||||
{
|
||||
const float EPS = 1e-6f;
|
||||
float v0[3], v1[3], v2[3];
|
||||
|
||||
dtVsub(v0, c,a);
|
||||
dtVsub(v1, b,a);
|
||||
dtVsub(v2, p,a);
|
||||
dtVsub(v0, c, a);
|
||||
dtVsub(v1, b, a);
|
||||
dtVsub(v2, p, a);
|
||||
|
||||
// Compute scaled barycentric coordinates
|
||||
float denom = v0[0] * v1[2] - v0[2] * v1[0];
|
||||
if (fabsf(denom) < EPS)
|
||||
return false;
|
||||
|
||||
float u = v1[2] * v2[0] - v1[0] * v2[2];
|
||||
float v = v0[0] * v2[2] - v0[2] * v2[0];
|
||||
|
||||
@@ -220,13 +231,9 @@ bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b
|
||||
v = -v;
|
||||
}
|
||||
|
||||
// The (sloppy) epsilon is needed to allow to get height of points which
|
||||
// are interpolated along the edges of the triangles.
|
||||
float epsilon = - 1e-4f * denom;
|
||||
|
||||
// If point lies inside the triangle, return interpolated ycoord.
|
||||
if (u >= epsilon && v >= epsilon && (u+v) <= denom - epsilon) {
|
||||
h = a[1] + (v0[1]*u + v1[1]*v) / denom;
|
||||
if (u >= 0.0f && v >= 0.0f && (u + v) <= denom) {
|
||||
h = a[1] + (v0[1] * u + v1[1] * v) / denom;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -283,6 +283,28 @@ inline bool dtVequal(const float* p0, const float* p1)
|
||||
return d < thr;
|
||||
}
|
||||
|
||||
/// Checks that the specified vector's components are all finite.
|
||||
/// @param[in] v A point. [(x, y, z)]
|
||||
/// @return True if all of the point's components are finite, i.e. not NaN
|
||||
/// or any of the infinities.
|
||||
inline bool dtVisfinite(const float* v)
|
||||
{
|
||||
bool result =
|
||||
dtMathIsfinite(v[0]) &&
|
||||
dtMathIsfinite(v[1]) &&
|
||||
dtMathIsfinite(v[2]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Checks that the specified vector's 2D components are finite.
|
||||
/// @param[in] v A point. [(x, y, z)]
|
||||
inline bool dtVisfinite2D(const float* v)
|
||||
{
|
||||
bool result = dtMathIsfinite(v[0]) && dtMathIsfinite(v[2]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Derives the dot product of two vectors on the xz-plane. (@p u . @p v)
|
||||
/// @param[in] u A vector [(x, y, z)]
|
||||
/// @param[in] v A vector [(x, y, z)]
|
||||
@@ -395,6 +417,8 @@ bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nve
|
||||
|
||||
float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t);
|
||||
|
||||
float dtDistancePtPtSqr2D(const float* pt, const float* p);
|
||||
|
||||
/// Derives the centroid of a convex polygon.
|
||||
/// @param[out] tc The centroid of the polgyon. [(x, y, z)]
|
||||
/// @param[in] idx The polygon indices. [(vertIndex) * @p nidx]
|
||||
|
||||
@@ -1409,12 +1409,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
|
||||
}
|
||||
|
||||
// Update agents using off-mesh connection.
|
||||
for (int i = 0; i < m_maxAgents; ++i)
|
||||
for (int i = 0; i < nagents; ++i)
|
||||
{
|
||||
dtCrowdAgentAnimation* anim = &m_agentAnims[i];
|
||||
dtCrowdAgent* ag = agents[i];
|
||||
const int idx = (int)(ag - m_agents);
|
||||
dtCrowdAgentAnimation* anim = &m_agentAnims[idx];
|
||||
if (!anim->active)
|
||||
continue;
|
||||
dtCrowdAgent* ag = agents[i];
|
||||
|
||||
|
||||
anim->t += dt;
|
||||
if (anim->t > anim->tmax)
|
||||
|
||||
@@ -16,5 +16,6 @@ inline float dtMathCeilf(float x) { return ceilf(x); }
|
||||
inline float dtMathCosf(float x) { return cosf(x); }
|
||||
inline float dtMathSinf(float x) { return sinf(x); }
|
||||
inline float dtMathAtan2f(float y, float x) { return atan2f(y, x); }
|
||||
inline bool dtMathIsfinite(float x) { return isfinite(x); }
|
||||
|
||||
#endif
|
||||
|
||||
167
Source/ThirdParty/recastnavigation/DetourNavMesh.cpp
vendored
167
Source/ThirdParty/recastnavigation/DetourNavMesh.cpp
vendored
@@ -616,63 +616,84 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
|
||||
}
|
||||
}
|
||||
|
||||
void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
|
||||
namespace
|
||||
{
|
||||
const dtMeshTile* tile = 0;
|
||||
const dtPoly* poly = 0;
|
||||
getTileAndPolyByRefUnsafe(ref, &tile, &poly);
|
||||
|
||||
// Off-mesh connections don't have detail polygons.
|
||||
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
template<bool onlyBoundary>
|
||||
void closestPointOnDetailEdges(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest)
|
||||
{
|
||||
const float* v0 = &tile->verts[poly->verts[0]*3];
|
||||
const float* v1 = &tile->verts[poly->verts[1]*3];
|
||||
const float d0 = dtVdist(pos, v0);
|
||||
const float d1 = dtVdist(pos, v1);
|
||||
const float u = d0 / (d0+d1);
|
||||
dtVlerp(closest, v0, v1, u);
|
||||
if (posOverPoly)
|
||||
*posOverPoly = false;
|
||||
return;
|
||||
const unsigned int ip = (unsigned int)(poly - tile->polys);
|
||||
const dtPolyDetail* pd = &tile->detailMeshes[ip];
|
||||
|
||||
float dmin = FLT_MAX;
|
||||
float tmin = 0;
|
||||
const float* pmin = 0;
|
||||
const float* pmax = 0;
|
||||
|
||||
for (int i = 0; i < pd->triCount; i++)
|
||||
{
|
||||
const unsigned char* tris = &tile->detailTris[(pd->triBase + i) * 4];
|
||||
const int ANY_BOUNDARY_EDGE =
|
||||
(DT_DETAIL_EDGE_BOUNDARY << 0) |
|
||||
(DT_DETAIL_EDGE_BOUNDARY << 2) |
|
||||
(DT_DETAIL_EDGE_BOUNDARY << 4);
|
||||
if (onlyBoundary && (tris[3] & ANY_BOUNDARY_EDGE) == 0)
|
||||
continue;
|
||||
|
||||
const float* v[3];
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
if (tris[j] < poly->vertCount)
|
||||
v[j] = &tile->verts[poly->verts[tris[j]] * 3];
|
||||
else
|
||||
v[j] = &tile->detailVerts[(pd->vertBase + (tris[j] - poly->vertCount)) * 3];
|
||||
}
|
||||
|
||||
for (int k = 0, j = 2; k < 3; j = k++)
|
||||
{
|
||||
if ((dtGetDetailTriEdgeFlags(tris[3], j) & DT_DETAIL_EDGE_BOUNDARY) == 0 &&
|
||||
(onlyBoundary || tris[j] < tris[k]))
|
||||
{
|
||||
// Only looking at boundary edges and this is internal, or
|
||||
// this is an inner edge that we will see again or have already seen.
|
||||
continue;
|
||||
}
|
||||
|
||||
float t;
|
||||
float d = dtDistancePtSegSqr2D(pos, v[j], v[k], t);
|
||||
if (d < dmin)
|
||||
{
|
||||
dmin = d;
|
||||
tmin = t;
|
||||
pmin = v[j];
|
||||
pmax = v[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dtVlerp(closest, pmin, pmax, tmin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool dtNavMesh::getPolyHeight(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const
|
||||
{
|
||||
// Off-mesh connections do not have detail polys and getting height
|
||||
// over them does not make sense.
|
||||
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
return false;
|
||||
|
||||
const unsigned int ip = (unsigned int)(poly - tile->polys);
|
||||
const dtPolyDetail* pd = &tile->detailMeshes[ip];
|
||||
|
||||
// Clamp point to be inside the polygon.
|
||||
float verts[DT_VERTS_PER_POLYGON*3];
|
||||
float edged[DT_VERTS_PER_POLYGON];
|
||||
float edget[DT_VERTS_PER_POLYGON];
|
||||
const int nv = poly->vertCount;
|
||||
for (int i = 0; i < nv; ++i)
|
||||
dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]);
|
||||
|
||||
dtVcopy(closest, pos);
|
||||
if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
|
||||
{
|
||||
// Point is outside the polygon, dtClamp to nearest edge.
|
||||
float dmin = edged[0];
|
||||
int imin = 0;
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
if (edged[i] < dmin)
|
||||
{
|
||||
dmin = edged[i];
|
||||
imin = i;
|
||||
}
|
||||
}
|
||||
const float* va = &verts[imin*3];
|
||||
const float* vb = &verts[((imin+1)%nv)*3];
|
||||
dtVlerp(closest, va, vb, edget[imin]);
|
||||
|
||||
if (posOverPoly)
|
||||
*posOverPoly = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (posOverPoly)
|
||||
*posOverPoly = true;
|
||||
}
|
||||
if (!dtPointInPolygon(pos, verts, nv))
|
||||
return false;
|
||||
|
||||
if (!height)
|
||||
return true;
|
||||
|
||||
// Find height at the location.
|
||||
for (int j = 0; j < pd->triCount; ++j)
|
||||
@@ -687,12 +708,53 @@ void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* close
|
||||
v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
|
||||
}
|
||||
float h;
|
||||
if (dtClosestHeightPointTriangle(closest, v[0], v[1], v[2], h))
|
||||
if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
|
||||
{
|
||||
closest[1] = h;
|
||||
break;
|
||||
*height = h;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If all triangle checks failed above (can happen with degenerate triangles
|
||||
// or larger floating point values) the point is on an edge, so just select
|
||||
// closest. This should almost never happen so the extra iteration here is
|
||||
// ok.
|
||||
float closest[3];
|
||||
closestPointOnDetailEdges<false>(tile, poly, pos, closest);
|
||||
*height = closest[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
|
||||
{
|
||||
const dtMeshTile* tile = 0;
|
||||
const dtPoly* poly = 0;
|
||||
getTileAndPolyByRefUnsafe(ref, &tile, &poly);
|
||||
|
||||
dtVcopy(closest, pos);
|
||||
if (getPolyHeight(tile, poly, pos, &closest[1]))
|
||||
{
|
||||
if (posOverPoly)
|
||||
*posOverPoly = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (posOverPoly)
|
||||
*posOverPoly = false;
|
||||
|
||||
// Off-mesh connections don't have detail polygons.
|
||||
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
{
|
||||
const float* v0 = &tile->verts[poly->verts[0]*3];
|
||||
const float* v1 = &tile->verts[poly->verts[1]*3];
|
||||
float t;
|
||||
dtDistancePtSegSqr2D(pos, v0, v1, t);
|
||||
dtVlerp(closest, v0, v1, t);
|
||||
return;
|
||||
}
|
||||
|
||||
// Outside poly that is not an offmesh connection.
|
||||
closestPointOnDetailEdges<true>(tile, poly, pos, closest);
|
||||
}
|
||||
|
||||
dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
|
||||
@@ -852,6 +914,13 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
|
||||
return DT_FAILURE | DT_WRONG_MAGIC;
|
||||
if (header->version != DT_NAVMESH_VERSION)
|
||||
return DT_FAILURE | DT_WRONG_VERSION;
|
||||
|
||||
#ifndef DT_POLYREF64
|
||||
// Do not allow adding more polygons than specified in the NavMesh's maxPolys constraint.
|
||||
// Otherwise, the poly ID cannot be represented with the given number of bits.
|
||||
if (m_polyBits < dtIlog2(dtNextPow2((unsigned int)header->polyCount)))
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
#endif
|
||||
|
||||
// Make sure the location is free.
|
||||
if (getTileAt(header->x, header->y, header->layer))
|
||||
|
||||
@@ -130,6 +130,11 @@ enum dtRaycastOptions
|
||||
DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
|
||||
};
|
||||
|
||||
enum dtDetailTriEdgeFlags
|
||||
{
|
||||
DT_DETAIL_EDGE_BOUNDARY = 0x01, ///< Detail triangle edge is part of the poly boundary
|
||||
};
|
||||
|
||||
|
||||
/// Limit raycasting during any angle pahfinding
|
||||
/// The limit is given as a multiple of the character radius
|
||||
@@ -287,7 +292,8 @@ struct dtMeshTile
|
||||
/// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount]
|
||||
float* detailVerts;
|
||||
|
||||
/// The detail mesh's triangles. [(vertA, vertB, vertC) * dtMeshHeader::detailTriCount]
|
||||
/// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount].
|
||||
/// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags.
|
||||
unsigned char* detailTris;
|
||||
|
||||
/// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount]
|
||||
@@ -305,6 +311,15 @@ private:
|
||||
dtMeshTile& operator=(const dtMeshTile&);
|
||||
};
|
||||
|
||||
/// Get flags for edge in detail triangle.
|
||||
/// @param triFlags[in] The flags for the triangle (last component of detail vertices above).
|
||||
/// @param edgeIndex[in] The index of the first vertex of the edge. For instance, if 0,
|
||||
/// returns flags for edge AB.
|
||||
inline int dtGetDetailTriEdgeFlags(unsigned char triFlags, int edgeIndex)
|
||||
{
|
||||
return (triFlags >> (edgeIndex * 2)) & 0x3;
|
||||
}
|
||||
|
||||
/// Configuration parameters used to define multi-tile navigation meshes.
|
||||
/// The values are used to allocate space during the initialization of a navigation mesh.
|
||||
/// @see dtNavMesh::init()
|
||||
@@ -314,8 +329,8 @@ struct dtNavMeshParams
|
||||
float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)]
|
||||
float tileWidth; ///< The width of each tile. (Along the x-axis.)
|
||||
float tileHeight; ///< The height of each tile. (Along the z-axis.)
|
||||
int maxTiles; ///< The maximum number of tiles the navigation mesh can contain.
|
||||
int maxPolys; ///< The maximum number of polygons each tile can contain.
|
||||
int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits are needed to identify tiles and polygons uniquely.
|
||||
int maxPolys; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are needed to identify tiles and polygons uniquely.
|
||||
};
|
||||
|
||||
/// A navigation mesh based on tiles of convex polygons.
|
||||
@@ -636,6 +651,8 @@ private:
|
||||
/// Find nearest polygon within a tile.
|
||||
dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
|
||||
const float* halfExtents, float* nearestPt) const;
|
||||
/// Returns whether position is over the poly and the height at the position if so.
|
||||
bool getPolyHeight(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const;
|
||||
/// Returns closest point on polygon.
|
||||
void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
|
||||
|
||||
@@ -655,6 +672,8 @@ private:
|
||||
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
|
||||
unsigned int m_polyBits; ///< Number of poly bits in the tile ID.
|
||||
#endif
|
||||
|
||||
friend class dtNavMeshQuery;
|
||||
};
|
||||
|
||||
/// Allocates a navigation mesh object using the Detour allocator.
|
||||
|
||||
@@ -222,7 +222,10 @@ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*fr
|
||||
dtPolyRef* randomRef, float* randomPt) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
|
||||
|
||||
if (!filter || !frand || !randomRef || !randomPt)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
// Randomly pick one tile. Assume that all tiles cover roughly the same area.
|
||||
const dtMeshTile* tile = 0;
|
||||
float tsum = 0.0f;
|
||||
@@ -319,8 +322,13 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
|
||||
dtAssert(m_openList);
|
||||
|
||||
// Validate input
|
||||
if (!startRef || !m_nav->isValidPolyRef(startRef))
|
||||
if (!m_nav->isValidPolyRef(startRef) ||
|
||||
!centerPos || !dtVisfinite(centerPos) ||
|
||||
maxRadius < 0 || !dtMathIsfinite(maxRadius) ||
|
||||
!filter || !frand || !randomRef || !randomPt)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
const dtMeshTile* startTile = 0;
|
||||
const dtPoly* startPoly = 0;
|
||||
@@ -474,12 +482,15 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
|
||||
dtVcopy(&verts[j*3],v);
|
||||
}
|
||||
|
||||
const float s = frand();
|
||||
const float t = frand();
|
||||
|
||||
float pt[3];
|
||||
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
|
||||
|
||||
do
|
||||
{
|
||||
const float s = frand();
|
||||
const float t = frand();
|
||||
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
|
||||
}
|
||||
while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr);
|
||||
|
||||
float h = 0.0f;
|
||||
dtStatus stat = getPolyHeight(randomPolyRef, pt, &h);
|
||||
if (dtStatusFailed(status))
|
||||
@@ -506,85 +517,14 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
|
||||
dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
const dtMeshTile* tile = 0;
|
||||
const dtPoly* poly = 0;
|
||||
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
|
||||
if (!m_nav->isValidPolyRef(ref) ||
|
||||
!pos || !dtVisfinite(pos) ||
|
||||
!closest)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
if (!tile)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
// Off-mesh connections don't have detail polygons.
|
||||
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
{
|
||||
const float* v0 = &tile->verts[poly->verts[0]*3];
|
||||
const float* v1 = &tile->verts[poly->verts[1]*3];
|
||||
const float d0 = dtVdist(pos, v0);
|
||||
const float d1 = dtVdist(pos, v1);
|
||||
const float u = d0 / (d0+d1);
|
||||
dtVlerp(closest, v0, v1, u);
|
||||
if (posOverPoly)
|
||||
*posOverPoly = false;
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
const unsigned int ip = (unsigned int)(poly - tile->polys);
|
||||
const dtPolyDetail* pd = &tile->detailMeshes[ip];
|
||||
|
||||
// Clamp point to be inside the polygon.
|
||||
float verts[DT_VERTS_PER_POLYGON*3];
|
||||
float edged[DT_VERTS_PER_POLYGON];
|
||||
float edget[DT_VERTS_PER_POLYGON];
|
||||
const int nv = poly->vertCount;
|
||||
for (int i = 0; i < nv; ++i)
|
||||
dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]);
|
||||
|
||||
dtVcopy(closest, pos);
|
||||
if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
|
||||
{
|
||||
// Point is outside the polygon, dtClamp to nearest edge.
|
||||
float dmin = edged[0];
|
||||
int imin = 0;
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
if (edged[i] < dmin)
|
||||
{
|
||||
dmin = edged[i];
|
||||
imin = i;
|
||||
}
|
||||
}
|
||||
const float* va = &verts[imin*3];
|
||||
const float* vb = &verts[((imin+1)%nv)*3];
|
||||
dtVlerp(closest, va, vb, edget[imin]);
|
||||
|
||||
if (posOverPoly)
|
||||
*posOverPoly = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (posOverPoly)
|
||||
*posOverPoly = true;
|
||||
}
|
||||
|
||||
// Find height at the location.
|
||||
for (int j = 0; j < pd->triCount; ++j)
|
||||
{
|
||||
const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
|
||||
const float* v[3];
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
if (t[k] < poly->vertCount)
|
||||
v[k] = &tile->verts[poly->verts[t[k]]*3];
|
||||
else
|
||||
v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
|
||||
}
|
||||
float h;
|
||||
if (dtClosestHeightPointTriangle(closest, v[0], v[1], v[2], h))
|
||||
{
|
||||
closest[1] = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_nav->closestPointOnPoly(ref, pos, closest, posOverPoly);
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -607,6 +547,9 @@ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float*
|
||||
const dtPoly* poly = 0;
|
||||
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
if (!pos || !dtVisfinite(pos) || !closest)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
// Collect vertices.
|
||||
float verts[DT_VERTS_PER_POLYGON*3];
|
||||
@@ -648,7 +591,7 @@ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float*
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Will return #DT_FAILURE if the provided position is outside the xz-bounds
|
||||
/// Will return #DT_FAILURE | DT_INVALID_PARAM if the provided position is outside the xz-bounds
|
||||
/// of the polygon.
|
||||
///
|
||||
dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const
|
||||
@@ -659,44 +602,28 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h
|
||||
const dtPoly* poly = 0;
|
||||
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
|
||||
if (!pos || !dtVisfinite2D(pos))
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
// We used to return success for offmesh connections, but the
|
||||
// getPolyHeight in DetourNavMesh does not do this, so special
|
||||
// case it here.
|
||||
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
{
|
||||
const float* v0 = &tile->verts[poly->verts[0]*3];
|
||||
const float* v1 = &tile->verts[poly->verts[1]*3];
|
||||
const float d0 = dtVdist2D(pos, v0);
|
||||
const float d1 = dtVdist2D(pos, v1);
|
||||
const float u = d0 / (d0+d1);
|
||||
float t;
|
||||
dtDistancePtSegSqr2D(pos, v0, v1, t);
|
||||
if (height)
|
||||
*height = v0[1] + (v1[1] - v0[1]) * u;
|
||||
*height = v0[1] + (v1[1] - v0[1])*t;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned int ip = (unsigned int)(poly - tile->polys);
|
||||
const dtPolyDetail* pd = &tile->detailMeshes[ip];
|
||||
for (int j = 0; j < pd->triCount; ++j)
|
||||
{
|
||||
const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
|
||||
const float* v[3];
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
if (t[k] < poly->vertCount)
|
||||
v[k] = &tile->verts[poly->verts[t[k]]*3];
|
||||
else
|
||||
v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
|
||||
}
|
||||
float h;
|
||||
if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
|
||||
{
|
||||
if (height)
|
||||
*height = h;
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
return m_nav->getPolyHeight(tile, poly, pos, height)
|
||||
? DT_SUCCESS
|
||||
: DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
class dtFindNearestPolyQuery : public dtPolyQuery
|
||||
@@ -706,15 +633,17 @@ class dtFindNearestPolyQuery : public dtPolyQuery
|
||||
float m_nearestDistanceSqr;
|
||||
dtPolyRef m_nearestRef;
|
||||
float m_nearestPoint[3];
|
||||
bool m_overPoly;
|
||||
|
||||
public:
|
||||
dtFindNearestPolyQuery(const dtNavMeshQuery* query, const float* center)
|
||||
: m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint()
|
||||
: m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint(), m_overPoly(false)
|
||||
{
|
||||
}
|
||||
|
||||
dtPolyRef nearestRef() const { return m_nearestRef; }
|
||||
const float* nearestPoint() const { return m_nearestPoint; }
|
||||
bool isOverPoly() const { return m_overPoly; }
|
||||
|
||||
void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count)
|
||||
{
|
||||
@@ -748,6 +677,7 @@ public:
|
||||
|
||||
m_nearestDistanceSqr = d;
|
||||
m_nearestRef = ref;
|
||||
m_overPoly = posOverPoly;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -762,11 +692,22 @@ public:
|
||||
dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* nearestRef, float* nearestPt) const
|
||||
{
|
||||
return findNearestPoly(center, halfExtents, filter, nearestRef, nearestPt, NULL);
|
||||
}
|
||||
|
||||
// If center and nearestPt point to an equal position, isOverPoly will be true;
|
||||
// however there's also a special case of climb height inside the polygon (see dtFindNearestPolyQuery)
|
||||
dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* nearestRef, float* nearestPt, bool* isOverPoly) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
|
||||
if (!nearestRef)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
// queryPolygons below will check rest of params
|
||||
|
||||
dtFindNearestPolyQuery query(this, center);
|
||||
|
||||
@@ -778,7 +719,11 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfE
|
||||
// Only override nearestPt if we actually found a poly so the nearest point
|
||||
// is valid.
|
||||
if (nearestPt && *nearestRef)
|
||||
{
|
||||
dtVcopy(nearestPt, query.nearestPoint());
|
||||
if (isOverPoly)
|
||||
*isOverPoly = query.isOverPoly();
|
||||
}
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
@@ -972,8 +917,12 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExt
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
|
||||
if (!center || !halfExtents || !filter || !query)
|
||||
if (!center || !dtVisfinite(center) ||
|
||||
!halfExtents || !dtVisfinite(halfExtents) ||
|
||||
!filter || !query)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
dtVsub(bmin, center, halfExtents);
|
||||
@@ -1021,14 +970,20 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
|
||||
dtAssert(m_nav);
|
||||
dtAssert(m_nodePool);
|
||||
dtAssert(m_openList);
|
||||
|
||||
if (pathCount)
|
||||
*pathCount = 0;
|
||||
|
||||
if (!pathCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*pathCount = 0;
|
||||
|
||||
// Validate input
|
||||
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) ||
|
||||
!startPos || !endPos || !filter || maxPath <= 0 || !path || !pathCount)
|
||||
!startPos || !dtVisfinite(startPos) ||
|
||||
!endPos || !dtVisfinite(endPos) ||
|
||||
!filter || !path || maxPath <= 0)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (startRef == endRef)
|
||||
{
|
||||
@@ -1263,18 +1218,21 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef
|
||||
m_query.status = DT_FAILURE;
|
||||
m_query.startRef = startRef;
|
||||
m_query.endRef = endRef;
|
||||
dtVcopy(m_query.startPos, startPos);
|
||||
dtVcopy(m_query.endPos, endPos);
|
||||
if (startPos)
|
||||
dtVcopy(m_query.startPos, startPos);
|
||||
if (endPos)
|
||||
dtVcopy(m_query.endPos, endPos);
|
||||
m_query.filter = filter;
|
||||
m_query.options = options;
|
||||
m_query.raycastLimitSqr = FLT_MAX;
|
||||
|
||||
if (!startRef || !endRef)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
// Validate input
|
||||
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
|
||||
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) ||
|
||||
!startPos || !dtVisfinite(startPos) ||
|
||||
!endPos || !dtVisfinite(endPos) || !filter)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// trade quality with performance?
|
||||
if (options & DT_FINDPATH_ANY_ANGLE)
|
||||
@@ -1530,7 +1488,13 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
|
||||
|
||||
dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath)
|
||||
{
|
||||
if (!pathCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*pathCount = 0;
|
||||
|
||||
if (!path || maxPath <= 0)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
if (dtStatusFailed(m_query.status))
|
||||
{
|
||||
@@ -1615,12 +1579,13 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount,
|
||||
dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
|
||||
dtPolyRef* path, int* pathCount, const int maxPath)
|
||||
{
|
||||
if (!pathCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*pathCount = 0;
|
||||
|
||||
if (existingSize == 0)
|
||||
{
|
||||
return DT_FAILURE;
|
||||
}
|
||||
|
||||
if (!existing || existingSize <= 0 || !path || !pathCount || maxPath <= 0)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
if (dtStatusFailed(m_query.status))
|
||||
{
|
||||
@@ -1823,14 +1788,19 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
|
||||
int* straightPathCount, const int maxStraightPath, const int options) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
|
||||
|
||||
if (!straightPathCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*straightPathCount = 0;
|
||||
|
||||
if (!maxStraightPath)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
if (!path[0])
|
||||
|
||||
if (!startPos || !dtVisfinite(startPos) ||
|
||||
!endPos || !dtVisfinite(endPos) ||
|
||||
!path || pathSize <= 0 || !path[0] ||
|
||||
maxStraightPath <= 0)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
dtStatus stat = 0;
|
||||
|
||||
@@ -2070,13 +2040,19 @@ dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* start
|
||||
dtAssert(m_nav);
|
||||
dtAssert(m_tinyNodePool);
|
||||
|
||||
if (!visitedCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*visitedCount = 0;
|
||||
|
||||
// Validate input
|
||||
if (!startRef)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
if (!m_nav->isValidPolyRef(startRef))
|
||||
|
||||
if (!m_nav->isValidPolyRef(startRef) ||
|
||||
!startPos || !dtVisfinite(startPos) ||
|
||||
!endPos || !dtVisfinite(endPos) ||
|
||||
!filter || !resultPos || !visited ||
|
||||
maxVisitedSize <= 0)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
dtStatus status = DT_SUCCESS;
|
||||
|
||||
@@ -2484,16 +2460,23 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
|
||||
dtRaycastHit* hit, dtPolyRef prevRef) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
|
||||
|
||||
if (!hit)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
hit->t = 0;
|
||||
hit->pathCount = 0;
|
||||
hit->pathCost = 0;
|
||||
|
||||
// Validate input
|
||||
if (!startRef || !m_nav->isValidPolyRef(startRef))
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
if (prevRef && !m_nav->isValidPolyRef(prevRef))
|
||||
if (!m_nav->isValidPolyRef(startRef) ||
|
||||
!startPos || !dtVisfinite(startPos) ||
|
||||
!endPos || !dtVisfinite(endPos) ||
|
||||
!filter ||
|
||||
(prevRef && !m_nav->isValidPolyRef(prevRef)))
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
float dir[3], curPos[3], lastPos[3];
|
||||
float verts[DT_VERTS_PER_POLYGON*3+3];
|
||||
@@ -2735,11 +2718,18 @@ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float*
|
||||
dtAssert(m_nodePool);
|
||||
dtAssert(m_openList);
|
||||
|
||||
*resultCount = 0;
|
||||
|
||||
// Validate input
|
||||
if (!startRef || !m_nav->isValidPolyRef(startRef))
|
||||
if (!resultCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*resultCount = 0;
|
||||
|
||||
if (!m_nav->isValidPolyRef(startRef) ||
|
||||
!centerPos || !dtVisfinite(centerPos) ||
|
||||
radius < 0 || !dtMathIsfinite(radius) ||
|
||||
!filter || maxResult < 0)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
m_nodePool->clear();
|
||||
m_openList->clear();
|
||||
@@ -2901,8 +2891,18 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v
|
||||
dtAssert(m_nav);
|
||||
dtAssert(m_nodePool);
|
||||
dtAssert(m_openList);
|
||||
|
||||
|
||||
if (!resultCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*resultCount = 0;
|
||||
|
||||
if (!m_nav->isValidPolyRef(startRef) ||
|
||||
!verts || nverts < 3 ||
|
||||
!filter || maxResult < 0)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// Validate input
|
||||
if (!startRef || !m_nav->isValidPolyRef(startRef))
|
||||
@@ -3088,13 +3088,20 @@ dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float*
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
dtAssert(m_tinyNodePool);
|
||||
|
||||
|
||||
if (!resultCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*resultCount = 0;
|
||||
|
||||
// Validate input
|
||||
if (!startRef || !m_nav->isValidPolyRef(startRef))
|
||||
if (!m_nav->isValidPolyRef(startRef) ||
|
||||
!centerPos || !dtVisfinite(centerPos) ||
|
||||
radius < 0 || !dtMathIsfinite(radius) ||
|
||||
!filter || maxResult < 0)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
}
|
||||
|
||||
static const int MAX_STACK = 48;
|
||||
dtNode* stack[MAX_STACK];
|
||||
int nstack = 0;
|
||||
@@ -3301,13 +3308,19 @@ dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter*
|
||||
const int maxSegments) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
|
||||
if (!segmentCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*segmentCount = 0;
|
||||
|
||||
|
||||
const dtMeshTile* tile = 0;
|
||||
const dtPoly* poly = 0;
|
||||
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
if (!filter || !segmentVerts || maxSegments < 0)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
int n = 0;
|
||||
static const int MAX_INTERVAL = 16;
|
||||
@@ -3455,8 +3468,13 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen
|
||||
dtAssert(m_openList);
|
||||
|
||||
// Validate input
|
||||
if (!startRef || !m_nav->isValidPolyRef(startRef))
|
||||
if (!m_nav->isValidPolyRef(startRef) ||
|
||||
!centerPos || !dtVisfinite(centerPos) ||
|
||||
maxRadius < 0 || !dtMathIsfinite(maxRadius) ||
|
||||
!filter || !hitDist || !hitPos || !hitNormal)
|
||||
{
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
m_nodePool->clear();
|
||||
m_openList->clear();
|
||||
|
||||
@@ -120,8 +120,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Provides information about raycast hit
|
||||
/// filled by dtNavMeshQuery::raycast
|
||||
/// @ingroup detour
|
||||
@@ -316,15 +314,31 @@ public:
|
||||
///@{
|
||||
|
||||
/// Finds the polygon nearest to the specified center point.
|
||||
/// [opt] means the specified parameter can be a null pointer, in that case the output parameter will not be set.
|
||||
///
|
||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
||||
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
||||
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] nearestRef The reference id of the nearest polygon.
|
||||
/// @param[out] nearestPt The nearest point on the polygon. [opt] [(x, y, z)]
|
||||
/// @param[out] nearestRef The reference id of the nearest polygon. Will be set to 0 if no polygon is found.
|
||||
/// @param[out] nearestPt The nearest point on the polygon. Unchanged if no polygon is found. [opt] [(x, y, z)]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findNearestPoly(const float* center, const float* halfExtents,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* nearestRef, float* nearestPt) const;
|
||||
|
||||
/// Finds the polygon nearest to the specified center point.
|
||||
/// [opt] means the specified parameter can be a null pointer, in that case the output parameter will not be set.
|
||||
///
|
||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
||||
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] nearestRef The reference id of the nearest polygon. Will be set to 0 if no polygon is found.
|
||||
/// @param[out] nearestPt The nearest point on the polygon. Unchanged if no polygon is found. [opt] [(x, y, z)]
|
||||
/// @param[out] isOverPoly Set to true if the point's X/Z coordinate lies inside the polygon, false otherwise. Unchanged if no polygon is found. [opt]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findNearestPoly(const float* center, const float* halfExtents,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* nearestRef, float* nearestPt, bool* isOverPoly) const;
|
||||
|
||||
/// Finds polygons that overlap the search box.
|
||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
||||
|
||||
47
Source/ThirdParty/recastnavigation/RecastAlloc.h
vendored
47
Source/ThirdParty/recastnavigation/RecastAlloc.h
vendored
@@ -106,6 +106,8 @@ class rcVectorBase {
|
||||
// Creates an array of the given size, copies all of this vector's data into it, and returns it.
|
||||
T* allocate_and_copy(rcSizeType size);
|
||||
void resize_impl(rcSizeType size, const T* value);
|
||||
// Requires: min_capacity > m_cap.
|
||||
rcSizeType get_new_capacity(rcSizeType min_capacity);
|
||||
public:
|
||||
typedef rcSizeType size_type;
|
||||
typedef T value_type;
|
||||
@@ -196,8 +198,7 @@ void rcVectorBase<T, H>::push_back(const T& value) {
|
||||
return;
|
||||
}
|
||||
|
||||
rcAssert(RC_SIZE_MAX / 2 >= m_size);
|
||||
rcSizeType new_cap = m_size ? 2*m_size : 1;
|
||||
const rcSizeType new_cap = get_new_capacity(m_cap + 1);
|
||||
T* data = allocate_and_copy(new_cap);
|
||||
// construct between allocate and destroy+free in case value is
|
||||
// in this vector.
|
||||
@@ -208,25 +209,44 @@ void rcVectorBase<T, H>::push_back(const T& value) {
|
||||
rcFree(m_data);
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
template <typename T, rcAllocHint H>
|
||||
rcSizeType rcVectorBase<T, H>::get_new_capacity(rcSizeType min_capacity) {
|
||||
rcAssert(min_capacity <= RC_SIZE_MAX);
|
||||
if (rcUnlikely(m_cap >= RC_SIZE_MAX / 2))
|
||||
return RC_SIZE_MAX;
|
||||
return 2 * m_cap > min_capacity ? 2 * m_cap : min_capacity;
|
||||
}
|
||||
|
||||
template <typename T, rcAllocHint H>
|
||||
void rcVectorBase<T, H>::resize_impl(rcSizeType size, const T* value) {
|
||||
if (size < m_size) {
|
||||
destroy_range(size, m_size);
|
||||
m_size = size;
|
||||
} else if (size > m_size) {
|
||||
T* new_data = allocate_and_copy(size);
|
||||
// We defer deconstructing/freeing old data until after constructing
|
||||
// new elements in case "value" is there.
|
||||
if (value) {
|
||||
construct_range(new_data + m_size, new_data + size, *value);
|
||||
if (size <= m_cap) {
|
||||
if (value) {
|
||||
construct_range(m_data + m_size, m_data + size, *value);
|
||||
} else {
|
||||
construct_range(m_data + m_size, m_data + size);
|
||||
}
|
||||
m_size = size;
|
||||
} else {
|
||||
construct_range(new_data + m_size, new_data + size);
|
||||
const rcSizeType new_cap = get_new_capacity(size);
|
||||
T* new_data = allocate_and_copy(new_cap);
|
||||
// We defer deconstructing/freeing old data until after constructing
|
||||
// new elements in case "value" is there.
|
||||
if (value) {
|
||||
construct_range(new_data + m_size, new_data + size, *value);
|
||||
} else {
|
||||
construct_range(new_data + m_size, new_data + size);
|
||||
}
|
||||
destroy_range(0, m_size);
|
||||
rcFree(m_data);
|
||||
m_data = new_data;
|
||||
m_cap = new_cap;
|
||||
m_size = size;
|
||||
}
|
||||
destroy_range(0, m_size);
|
||||
rcFree(m_data);
|
||||
m_data = new_data;
|
||||
m_cap = size;
|
||||
m_size = size;
|
||||
}
|
||||
}
|
||||
template <typename T, rcAllocHint H>
|
||||
@@ -303,6 +323,7 @@ public:
|
||||
rcIntArray(int n) : m_impl(n, 0) {}
|
||||
void push(int item) { m_impl.push_back(item); }
|
||||
void resize(int size) { m_impl.resize(size); }
|
||||
void clear() { m_impl.clear(); }
|
||||
int pop()
|
||||
{
|
||||
int v = m_impl.back();
|
||||
|
||||
@@ -921,8 +921,8 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
continue;
|
||||
const unsigned char area = chf.areas[i];
|
||||
|
||||
verts.resize(0);
|
||||
simplified.resize(0);
|
||||
verts.clear();
|
||||
simplified.clear();
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
walkContour(x, y, i, chf, flags, verts);
|
||||
@@ -1009,7 +1009,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
if (cset.nconts > 0)
|
||||
{
|
||||
// Calculate winding of all polygons.
|
||||
rcScopedDelete<char> winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP));
|
||||
rcScopedDelete<signed char> winding((signed char*)rcAlloc(sizeof(signed char)*cset.nconts, RC_ALLOC_TEMP));
|
||||
if (!winding)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts);
|
||||
|
||||
@@ -653,8 +653,8 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
|
||||
for (int i = 0; i < nin; ++i)
|
||||
rcVcopy(&verts[i*3], &in[i*3]);
|
||||
|
||||
edges.resize(0);
|
||||
tris.resize(0);
|
||||
edges.clear();
|
||||
tris.clear();
|
||||
|
||||
const float cs = chf.cs;
|
||||
const float ics = 1.0f/cs;
|
||||
@@ -803,7 +803,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
|
||||
int x1 = (int)ceilf(bmax[0]/sampleDist);
|
||||
int z0 = (int)floorf(bmin[2]/sampleDist);
|
||||
int z1 = (int)ceilf(bmax[2]/sampleDist);
|
||||
samples.resize(0);
|
||||
samples.clear();
|
||||
for (int z = z0; z < z1; ++z)
|
||||
{
|
||||
for (int x = x0; x < x1; ++x)
|
||||
@@ -864,8 +864,8 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
|
||||
|
||||
// Create new triangulation.
|
||||
// TODO: Incremental add instead of full rebuild.
|
||||
edges.resize(0);
|
||||
tris.resize(0);
|
||||
edges.clear();
|
||||
tris.clear();
|
||||
delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
|
||||
}
|
||||
}
|
||||
@@ -935,7 +935,7 @@ static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield&
|
||||
pcy /= npoly;
|
||||
|
||||
// Use seeds array as a stack for DFS
|
||||
array.resize(0);
|
||||
array.clear();
|
||||
array.push(startCellX);
|
||||
array.push(startCellY);
|
||||
array.push(startSpanIndex);
|
||||
@@ -1001,7 +1001,7 @@ static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield&
|
||||
rcSwap(dirs[directDir], dirs[3]);
|
||||
}
|
||||
|
||||
array.resize(0);
|
||||
array.clear();
|
||||
// getHeightData seeds are given in coordinates with borders
|
||||
array.push(cx+bs);
|
||||
array.push(cy+bs);
|
||||
@@ -1030,7 +1030,7 @@ static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf,
|
||||
// Note: Reads to the compact heightfield are offset by border size (bs)
|
||||
// since border size offset is already removed from the polymesh vertices.
|
||||
|
||||
queue.resize(0);
|
||||
queue.clear();
|
||||
// Set all heights to RC_UNSET_HEIGHT.
|
||||
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
|
||||
|
||||
@@ -1141,7 +1141,8 @@ static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf,
|
||||
static unsigned char getEdgeFlags(const float* va, const float* vb,
|
||||
const float* vpoly, const int npoly)
|
||||
{
|
||||
// Return true if edge (va,vb) is part of the polygon.
|
||||
// The flag returned by this function matches dtDetailTriEdgeFlags in Detour.
|
||||
// Figure out if edge (va,vb) is part of the polygon boundary.
|
||||
static const float thrSqr = rcSqr(0.001f);
|
||||
for (int i = 0, j = npoly-1; i < npoly; j=i++)
|
||||
{
|
||||
|
||||
@@ -650,7 +650,7 @@ static bool mergeRegions(rcRegion& rega, rcRegion& regb)
|
||||
return false;
|
||||
|
||||
// Merge neighbours.
|
||||
rega.connections.resize(0);
|
||||
rega.connections.clear();
|
||||
for (int i = 0, ni = acon.size(); i < ni-1; ++i)
|
||||
rega.connections.push(acon[(insa+1+i) % ni]);
|
||||
|
||||
@@ -876,8 +876,8 @@ static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRe
|
||||
// Also keep track of the regions connects to a tile border.
|
||||
bool connectsToBorder = false;
|
||||
int spanCount = 0;
|
||||
stack.resize(0);
|
||||
trace.resize(0);
|
||||
stack.clear();
|
||||
trace.clear();
|
||||
|
||||
reg.visited = true;
|
||||
stack.push(i);
|
||||
@@ -1068,7 +1068,7 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
lregs.resize(0);
|
||||
lregs.clear();
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
@@ -1139,7 +1139,7 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
|
||||
// Start search.
|
||||
root.id = layerId;
|
||||
|
||||
stack.resize(0);
|
||||
stack.clear();
|
||||
stack.push(i);
|
||||
|
||||
while (stack.size() > 0)
|
||||
|
||||
2
Source/ThirdParty/stb/stb.Build.cs
vendored
2
Source/ThirdParty/stb/stb.Build.cs
vendored
@@ -14,7 +14,7 @@ public class stb : HeaderOnlyModule
|
||||
base.Init();
|
||||
|
||||
LicenseType = LicenseTypes.MIT;
|
||||
LicenseFilePath = "LICENSE.txt";
|
||||
LicenseFilePath = "LICENSE";
|
||||
|
||||
// Merge third-party modules into engine binary
|
||||
BinaryModuleName = "FlaxEngine";
|
||||
|
||||
457
Source/ThirdParty/stb/stb_image.h
vendored
457
Source/ThirdParty/stb/stb_image.h
vendored
File diff suppressed because it is too large
Load Diff
2631
Source/ThirdParty/stb/stb_image_resize.h
vendored
Normal file
2631
Source/ThirdParty/stb/stb_image_resize.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
241
Source/ThirdParty/stb/stb_image_write.h
vendored
241
Source/ThirdParty/stb/stb_image_write.h
vendored
@@ -1,4 +1,4 @@
|
||||
/* stb_image_write - v1.13 - public domain - http://nothings.org/stb
|
||||
/* stb_image_write - v1.15 - public domain - http://nothings.org/stb
|
||||
writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
|
||||
no warranty implied; use at your own risk
|
||||
|
||||
@@ -105,7 +105,7 @@ USAGE:
|
||||
|
||||
TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
|
||||
data, set the global variable 'stbi_write_tga_with_rle' to 0.
|
||||
|
||||
|
||||
JPEG does ignore alpha channels in input data; quality is between 1 and 100.
|
||||
Higher quality looks better but results in a bigger image.
|
||||
JPEG baseline (no JPEG progressive).
|
||||
@@ -113,7 +113,7 @@ USAGE:
|
||||
CREDITS:
|
||||
|
||||
|
||||
Sean Barrett - PNG/BMP/TGA
|
||||
Sean Barrett - PNG/BMP/TGA
|
||||
Baldur Karlsson - HDR
|
||||
Jean-Sebastien Guay - TGA monochrome
|
||||
Tim Kelsey - misc enhancements
|
||||
@@ -247,17 +247,17 @@ STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);
|
||||
#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)
|
||||
|
||||
#ifdef STB_IMAGE_WRITE_STATIC
|
||||
static int stbi__flip_vertically_on_write=0;
|
||||
static int stbi_write_png_compression_level = 8;
|
||||
static int stbi_write_tga_with_rle = 1;
|
||||
static int stbi_write_force_png_filter = -1;
|
||||
#else
|
||||
int stbi_write_png_compression_level = 8;
|
||||
int stbi__flip_vertically_on_write=0;
|
||||
int stbi_write_tga_with_rle = 1;
|
||||
int stbi_write_force_png_filter = -1;
|
||||
#endif
|
||||
|
||||
static int stbi__flip_vertically_on_write = 0;
|
||||
|
||||
STBIWDEF void stbi_flip_vertically_on_write(int flag)
|
||||
{
|
||||
stbi__flip_vertically_on_write = flag;
|
||||
@@ -267,6 +267,8 @@ typedef struct
|
||||
{
|
||||
stbi_write_func *func;
|
||||
void *context;
|
||||
unsigned char buffer[64];
|
||||
int buf_used;
|
||||
} stbi__write_context;
|
||||
|
||||
// initialize a callback-based context
|
||||
@@ -306,7 +308,7 @@ static FILE *stbiw__fopen(char const *filename, char const *mode)
|
||||
wchar_t wFilename[1024];
|
||||
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
|
||||
return 0;
|
||||
|
||||
|
||||
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
|
||||
return 0;
|
||||
|
||||
@@ -380,16 +382,36 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
|
||||
va_end(v);
|
||||
}
|
||||
|
||||
static void stbiw__write_flush(stbi__write_context *s)
|
||||
{
|
||||
if (s->buf_used) {
|
||||
s->func(s->context, &s->buffer, s->buf_used);
|
||||
s->buf_used = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void stbiw__putc(stbi__write_context *s, unsigned char c)
|
||||
{
|
||||
s->func(s->context, &c, 1);
|
||||
}
|
||||
|
||||
static void stbiw__write1(stbi__write_context *s, unsigned char a)
|
||||
{
|
||||
if (s->buf_used + 1 > sizeof(s->buffer))
|
||||
stbiw__write_flush(s);
|
||||
s->buffer[s->buf_used++] = a;
|
||||
}
|
||||
|
||||
static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
|
||||
{
|
||||
unsigned char arr[3];
|
||||
arr[0] = a; arr[1] = b; arr[2] = c;
|
||||
s->func(s->context, arr, 3);
|
||||
int n;
|
||||
if (s->buf_used + 3 > sizeof(s->buffer))
|
||||
stbiw__write_flush(s);
|
||||
n = s->buf_used;
|
||||
s->buf_used = n+3;
|
||||
s->buffer[n+0] = a;
|
||||
s->buffer[n+1] = b;
|
||||
s->buffer[n+2] = c;
|
||||
}
|
||||
|
||||
static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
|
||||
@@ -398,7 +420,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, in
|
||||
int k;
|
||||
|
||||
if (write_alpha < 0)
|
||||
s->func(s->context, &d[comp - 1], 1);
|
||||
stbiw__write1(s, d[comp - 1]);
|
||||
|
||||
switch (comp) {
|
||||
case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
|
||||
@@ -406,7 +428,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, in
|
||||
if (expand_mono)
|
||||
stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
|
||||
else
|
||||
s->func(s->context, d, 1); // monochrome TGA
|
||||
stbiw__write1(s, d[0]); // monochrome TGA
|
||||
break;
|
||||
case 4:
|
||||
if (!write_alpha) {
|
||||
@@ -422,7 +444,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, in
|
||||
break;
|
||||
}
|
||||
if (write_alpha > 0)
|
||||
s->func(s->context, &d[comp - 1], 1);
|
||||
stbiw__write1(s, d[comp - 1]);
|
||||
}
|
||||
|
||||
static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
|
||||
@@ -447,6 +469,7 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, i
|
||||
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
|
||||
stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
|
||||
}
|
||||
stbiw__write_flush(s);
|
||||
s->func(s->context, &zero, scanline_pad);
|
||||
}
|
||||
}
|
||||
@@ -476,7 +499,7 @@ static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, c
|
||||
|
||||
STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
|
||||
{
|
||||
stbi__write_context s;
|
||||
stbi__write_context s = { 0 };
|
||||
stbi__start_write_callbacks(&s, func, context);
|
||||
return stbi_write_bmp_core(&s, x, y, comp, data);
|
||||
}
|
||||
@@ -484,7 +507,7 @@ STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x,
|
||||
#ifndef STBI_WRITE_NO_STDIO
|
||||
STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
|
||||
{
|
||||
stbi__write_context s;
|
||||
stbi__write_context s = { 0 };
|
||||
if (stbi__start_write_file(&s,filename)) {
|
||||
int r = stbi_write_bmp_core(&s, x, y, comp, data);
|
||||
stbi__end_write_file(&s);
|
||||
@@ -557,24 +580,25 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v
|
||||
|
||||
if (diff) {
|
||||
unsigned char header = STBIW_UCHAR(len - 1);
|
||||
s->func(s->context, &header, 1);
|
||||
stbiw__write1(s, header);
|
||||
for (k = 0; k < len; ++k) {
|
||||
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
|
||||
}
|
||||
} else {
|
||||
unsigned char header = STBIW_UCHAR(len - 129);
|
||||
s->func(s->context, &header, 1);
|
||||
stbiw__write1(s, header);
|
||||
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
stbiw__write_flush(s);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
|
||||
{
|
||||
stbi__write_context s;
|
||||
stbi__write_context s = { 0 };
|
||||
stbi__start_write_callbacks(&s, func, context);
|
||||
return stbi_write_tga_core(&s, x, y, comp, (void *) data);
|
||||
}
|
||||
@@ -582,7 +606,7 @@ STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x,
|
||||
#ifndef STBI_WRITE_NO_STDIO
|
||||
STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
|
||||
{
|
||||
stbi__write_context s;
|
||||
stbi__write_context s = { 0 };
|
||||
if (stbi__start_write_file(&s,filename)) {
|
||||
int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
|
||||
stbi__end_write_file(&s);
|
||||
@@ -615,7 +639,6 @@ static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef STBI_WRITE_NO_STDIO
|
||||
static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)
|
||||
{
|
||||
unsigned char lengthbyte = STBIW_UCHAR(length+128);
|
||||
@@ -749,14 +772,15 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f
|
||||
|
||||
STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)
|
||||
{
|
||||
stbi__write_context s;
|
||||
stbi__write_context s = { 0 };
|
||||
stbi__start_write_callbacks(&s, func, context);
|
||||
return stbi_write_hdr_core(&s, x, y, comp, (float *) data);
|
||||
}
|
||||
|
||||
#ifndef STBI_WRITE_NO_STDIO
|
||||
STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
|
||||
{
|
||||
stbi__write_context s;
|
||||
stbi__write_context s = { 0 };
|
||||
if (stbi__start_write_file(&s,filename)) {
|
||||
int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
|
||||
stbi__end_write_file(&s);
|
||||
@@ -774,7 +798,7 @@ STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const
|
||||
|
||||
#ifndef STBIW_ZLIB_COMPRESS
|
||||
// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
|
||||
#define stbiw__sbraw(a) ((int *) (a) - 2)
|
||||
#define stbiw__sbraw(a) ((int *) (void *) (a) - 2)
|
||||
#define stbiw__sbm(a) stbiw__sbraw(a)[0]
|
||||
#define stbiw__sbn(a) stbiw__sbraw(a)[1]
|
||||
|
||||
@@ -1044,13 +1068,13 @@ static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int
|
||||
int type = mymap[filter_type];
|
||||
unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y);
|
||||
int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;
|
||||
|
||||
|
||||
if (type==0) {
|
||||
memcpy(line_buffer, z, width*n);
|
||||
return;
|
||||
}
|
||||
|
||||
// first loop isn't optimized since it's just one pixel
|
||||
// first loop isn't optimized since it's just one pixel
|
||||
for (i = 0; i < n; ++i) {
|
||||
switch (type) {
|
||||
case 1: line_buffer[i] = z[i]; break;
|
||||
@@ -1271,26 +1295,31 @@ static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {
|
||||
bits[0] = val & ((1<<bits[1])-1);
|
||||
}
|
||||
|
||||
static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) {
|
||||
static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, int du_stride, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) {
|
||||
const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] };
|
||||
const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] };
|
||||
int dataOff, i, diff, end0pos;
|
||||
int dataOff, i, j, n, diff, end0pos, x, y;
|
||||
int DU[64];
|
||||
|
||||
// DCT rows
|
||||
for(dataOff=0; dataOff<64; dataOff+=8) {
|
||||
for(dataOff=0, n=du_stride*8; dataOff<n; dataOff+=du_stride) {
|
||||
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);
|
||||
}
|
||||
// DCT columns
|
||||
for(dataOff=0; dataOff<8; ++dataOff) {
|
||||
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+8], &CDU[dataOff+16], &CDU[dataOff+24], &CDU[dataOff+32], &CDU[dataOff+40], &CDU[dataOff+48], &CDU[dataOff+56]);
|
||||
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+du_stride], &CDU[dataOff+du_stride*2], &CDU[dataOff+du_stride*3], &CDU[dataOff+du_stride*4],
|
||||
&CDU[dataOff+du_stride*5], &CDU[dataOff+du_stride*6], &CDU[dataOff+du_stride*7]);
|
||||
}
|
||||
// Quantize/descale/zigzag the coefficients
|
||||
for(i=0; i<64; ++i) {
|
||||
float v = CDU[i]*fdtbl[i];
|
||||
// DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
|
||||
// ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
|
||||
DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);
|
||||
for(y = 0, j=0; y < 8; ++y) {
|
||||
for(x = 0; x < 8; ++x,++j) {
|
||||
float v;
|
||||
i = y*du_stride+x;
|
||||
v = CDU[i]*fdtbl[j];
|
||||
// DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
|
||||
// ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
|
||||
DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
// Encode DC
|
||||
@@ -1405,10 +1434,10 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in
|
||||
37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};
|
||||
static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,
|
||||
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};
|
||||
static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
|
||||
static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
|
||||
1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };
|
||||
|
||||
int row, col, i, k;
|
||||
int row, col, i, k, subsample;
|
||||
float fdtbl_Y[64], fdtbl_UV[64];
|
||||
unsigned char YTable[64], UVTable[64];
|
||||
|
||||
@@ -1417,6 +1446,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in
|
||||
}
|
||||
|
||||
quality = quality ? quality : 90;
|
||||
subsample = quality <= 90 ? 1 : 0;
|
||||
quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;
|
||||
quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
|
||||
|
||||
@@ -1439,7 +1469,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in
|
||||
static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };
|
||||
static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };
|
||||
const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),
|
||||
3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };
|
||||
3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };
|
||||
s->func(s->context, (void*)head0, sizeof(head0));
|
||||
s->func(s->context, (void*)YTable, sizeof(YTable));
|
||||
stbiw__putc(s, 1);
|
||||
@@ -1462,36 +1492,74 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in
|
||||
// Encode 8x8 macroblocks
|
||||
{
|
||||
static const unsigned short fillBits[] = {0x7F, 7};
|
||||
const unsigned char *imageData = (const unsigned char *)data;
|
||||
int DCY=0, DCU=0, DCV=0;
|
||||
int bitBuf=0, bitCnt=0;
|
||||
// comp == 2 is grey+alpha (alpha is ignored)
|
||||
int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
|
||||
const unsigned char *dataR = (const unsigned char *)data;
|
||||
const unsigned char *dataG = dataR + ofsG;
|
||||
const unsigned char *dataB = dataR + ofsB;
|
||||
int x, y, pos;
|
||||
for(y = 0; y < height; y += 8) {
|
||||
for(x = 0; x < width; x += 8) {
|
||||
float YDU[64], UDU[64], VDU[64];
|
||||
for(row = y, pos = 0; row < y+8; ++row) {
|
||||
// row >= height => use last input row
|
||||
int clamped_row = (row < height) ? row : height - 1;
|
||||
int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
|
||||
for(col = x; col < x+8; ++col, ++pos) {
|
||||
float r, g, b;
|
||||
// if col >= width => use pixel from last input column
|
||||
int p = base_p + ((col < width) ? col : (width-1))*comp;
|
||||
if(subsample) {
|
||||
for(y = 0; y < height; y += 16) {
|
||||
for(x = 0; x < width; x += 16) {
|
||||
float Y[256], U[256], V[256];
|
||||
for(row = y, pos = 0; row < y+16; ++row) {
|
||||
// row >= height => use last input row
|
||||
int clamped_row = (row < height) ? row : height - 1;
|
||||
int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
|
||||
for(col = x; col < x+16; ++col, ++pos) {
|
||||
// if col >= width => use pixel from last input column
|
||||
int p = base_p + ((col < width) ? col : (width-1))*comp;
|
||||
float r = dataR[p], g = dataG[p], b = dataB[p];
|
||||
Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
|
||||
U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
|
||||
V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
|
||||
}
|
||||
}
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
|
||||
|
||||
r = imageData[p+0];
|
||||
g = imageData[p+ofsG];
|
||||
b = imageData[p+ofsB];
|
||||
YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128;
|
||||
UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b;
|
||||
VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b;
|
||||
// subsample U,V
|
||||
{
|
||||
float subU[64], subV[64];
|
||||
int yy, xx;
|
||||
for(yy = 0, pos = 0; yy < 8; ++yy) {
|
||||
for(xx = 0; xx < 8; ++xx, ++pos) {
|
||||
int j = yy*32+xx*2;
|
||||
subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f;
|
||||
subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f;
|
||||
}
|
||||
}
|
||||
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
|
||||
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(y = 0; y < height; y += 8) {
|
||||
for(x = 0; x < width; x += 8) {
|
||||
float Y[64], U[64], V[64];
|
||||
for(row = y, pos = 0; row < y+8; ++row) {
|
||||
// row >= height => use last input row
|
||||
int clamped_row = (row < height) ? row : height - 1;
|
||||
int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
|
||||
for(col = x; col < x+8; ++col, ++pos) {
|
||||
// if col >= width => use pixel from last input column
|
||||
int p = base_p + ((col < width) ? col : (width-1))*comp;
|
||||
float r = dataR[p], g = dataG[p], b = dataB[p];
|
||||
Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
|
||||
U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
|
||||
V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
|
||||
}
|
||||
}
|
||||
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
|
||||
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
|
||||
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT);
|
||||
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
|
||||
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1508,7 +1576,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in
|
||||
|
||||
STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality)
|
||||
{
|
||||
stbi__write_context s;
|
||||
stbi__write_context s = { 0 };
|
||||
stbi__start_write_callbacks(&s, func, context);
|
||||
return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality);
|
||||
}
|
||||
@@ -1517,7 +1585,7 @@ STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x,
|
||||
#ifndef STBI_WRITE_NO_STDIO
|
||||
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)
|
||||
{
|
||||
stbi__write_context s;
|
||||
stbi__write_context s = { 0 };
|
||||
if (stbi__start_write_file(&s,filename)) {
|
||||
int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
|
||||
stbi__end_write_file(&s);
|
||||
@@ -1530,10 +1598,13 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const
|
||||
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
||||
/* Revision history
|
||||
1.14 (2020-02-02) updated JPEG writer to downsample chroma channels
|
||||
1.13
|
||||
1.12
|
||||
1.11 (2019-08-11)
|
||||
|
||||
|
||||
1.10 (2019-02-07)
|
||||
support utf8 filenames in Windows; fix warnings and platform ifdefs
|
||||
support utf8 filenames in Windows; fix warnings and platform ifdefs
|
||||
1.09 (2018-02-11)
|
||||
fix typo in zlib quality API, improve STB_I_W_STATIC in C++
|
||||
1.08 (2018-01-29)
|
||||
@@ -1582,38 +1653,38 @@ This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -12,8 +12,9 @@ namespace Flax.Build.Bindings
|
||||
partial class BindingsGenerator
|
||||
{
|
||||
private static readonly bool[] CppParamsThatNeedLocalVariable = new bool[64];
|
||||
private static readonly bool[] CppParamsThatNeedConvertion = new bool[64];
|
||||
private static readonly string[] CppParamsThatNeedConvertionWrappers = new string[64];
|
||||
private static readonly bool[] CppParamsThatNeedConversion = new bool[64];
|
||||
private static readonly string[] CppParamsThatNeedConversionWrappers = new string[64];
|
||||
private static readonly string[] CppParamsThatNeedConversionTypes = new string[64];
|
||||
private static readonly string[] CppParamsWrappersCache = new string[64];
|
||||
public static readonly List<ApiTypeInfo> CppUsedNonPodTypes = new List<ApiTypeInfo>();
|
||||
private static readonly List<ApiTypeInfo> CppUsedNonPodTypesList = new List<ApiTypeInfo>();
|
||||
@@ -620,7 +621,7 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(", ");
|
||||
separator = true;
|
||||
|
||||
CppParamsThatNeedConvertion[i] = false;
|
||||
CppParamsThatNeedConversion[i] = false;
|
||||
CppParamsWrappersCache[i] = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, caller, out var managedType, functionInfo, out CppParamsThatNeedLocalVariable[i]);
|
||||
contents.Append(managedType);
|
||||
if (parameterInfo.IsRef || parameterInfo.IsOut || UsePassByReference(buildData, parameterInfo.Type, caller))
|
||||
@@ -654,8 +655,8 @@ namespace Flax.Build.Bindings
|
||||
if (convertOutputParameter)
|
||||
{
|
||||
useInlinedReturn = false;
|
||||
CppParamsThatNeedConvertion[i] = true;
|
||||
CppParamsThatNeedConvertionWrappers[i] = GenerateCppWrapperNativeToManaged(buildData, parameterInfo.Type, caller, out _, functionInfo);
|
||||
CppParamsThatNeedConversion[i] = true;
|
||||
CppParamsThatNeedConversionWrappers[i] = GenerateCppWrapperNativeToManaged(buildData, parameterInfo.Type, caller, out CppParamsThatNeedConversionTypes[i], functionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -717,7 +718,7 @@ namespace Flax.Build.Bindings
|
||||
callParams += ", ";
|
||||
separator = true;
|
||||
var name = parameterInfo.Name;
|
||||
if (CppParamsThatNeedConvertion[i] && (!FindApiTypeInfo(buildData, parameterInfo.Type, caller)?.IsStruct ?? false))
|
||||
if (CppParamsThatNeedConversion[i] && (!FindApiTypeInfo(buildData, parameterInfo.Type, caller)?.IsStruct ?? false))
|
||||
name = '*' + name;
|
||||
|
||||
string param = string.Empty;
|
||||
@@ -735,7 +736,7 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
|
||||
// Special case for output result parameters that needs additional converting from native to managed format (such as non-POD structures or output array parameter)
|
||||
if (CppParamsThatNeedConvertion[i])
|
||||
if (CppParamsThatNeedConversion[i])
|
||||
{
|
||||
var apiType = FindApiTypeInfo(buildData, parameterInfo.Type, caller);
|
||||
if (apiType != null)
|
||||
@@ -792,9 +793,41 @@ namespace Flax.Build.Bindings
|
||||
var parameterInfo = functionInfo.Parameters[i];
|
||||
|
||||
// Special case for output result parameters that needs additional converting from native to managed format (such as non-POD structures or output array parameter)
|
||||
if (CppParamsThatNeedConvertion[i])
|
||||
if (CppParamsThatNeedConversion[i])
|
||||
{
|
||||
contents.AppendFormat(" *{0} = {1};", parameterInfo.Name, string.Format(CppParamsThatNeedConvertionWrappers[i], parameterInfo.Name + "Temp")).AppendLine();
|
||||
var value = string.Format(CppParamsThatNeedConversionWrappers[i], parameterInfo.Name + "Temp");
|
||||
|
||||
// MonoObject* parameters returned by reference need write barrier for GC
|
||||
if (parameterInfo.IsOut)
|
||||
{
|
||||
var apiType = FindApiTypeInfo(buildData, parameterInfo.Type, caller);
|
||||
if (apiType != null)
|
||||
{
|
||||
if (apiType.IsClass)
|
||||
{
|
||||
contents.AppendFormat(" mono_gc_wbarrier_generic_store({0}, (MonoObject*){1});", parameterInfo.Name, value).AppendLine();
|
||||
continue;
|
||||
}
|
||||
if (apiType.IsStruct && !apiType.IsPod)
|
||||
{
|
||||
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MClass.h");
|
||||
contents.AppendFormat(" {{ auto _temp = {1}; mono_gc_wbarrier_value_copy({0}, &_temp, 1, {2}::TypeInitializer.GetType().ManagedClass->GetNative()); }}", parameterInfo.Name, value, apiType.FullNameNative).AppendLine();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// BytesContainer
|
||||
if (parameterInfo.Type.Type == "BytesContainer" && parameterInfo.Type.GenericArgs == null)
|
||||
{
|
||||
contents.AppendFormat(" mono_gc_wbarrier_generic_store({0}, (MonoObject*){1});", parameterInfo.Name, value).AppendLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Exception($"Unsupported type of parameter '{parameterInfo}' in method '{functionInfo}' to be passed using 'out'");
|
||||
}
|
||||
}
|
||||
contents.AppendFormat(" *{0} = {1};", parameterInfo.Name, value).AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace Flax.Build.Platforms
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DllExport => "__attribute__ ((__visibility__ (\"default\")))";
|
||||
public override string DllExport => "__attribute__((__visibility__(\\\"default\\\")))";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DllImport => "";
|
||||
|
||||
@@ -214,7 +214,7 @@ namespace Flax.Build.Projects.VisualStudioCode
|
||||
}
|
||||
case TargetPlatform.Linux:
|
||||
{
|
||||
json.AddField("command", "mono");
|
||||
json.AddField("command", Path.Combine(Globals.EngineRoot, "Source/Platforms/Editor/Linux/Mono/bin/mono"));
|
||||
json.BeginArray("args");
|
||||
{
|
||||
json.AddUnnamedField(buildToolPath);
|
||||
@@ -480,11 +480,13 @@ namespace Flax.Build.Projects.VisualStudioCode
|
||||
|
||||
// File and folders excludes
|
||||
json.BeginObject("files.exclude");
|
||||
json.AddField("**/.git", true);
|
||||
json.AddField("**/.svn", true);
|
||||
json.AddField("**/.hg", true);
|
||||
json.AddField("**/.vs", true);
|
||||
json.AddField("**/Binaries", true);
|
||||
json.AddField("**/Cache", true);
|
||||
json.AddField("**/packages", true);
|
||||
json.AddField("**/Content", true);
|
||||
json.AddField("**/Logs", true);
|
||||
json.AddField("**/Screenshots", true);
|
||||
json.AddField("**/Output", true);
|
||||
|
||||
Reference in New Issue
Block a user