Merge branch 'master' into 1.1
# Conflicts: # Source/Engine/Content/JsonAsset.h # Source/Engine/Core/Config/Settings.h
This commit is contained in:
@@ -162,8 +162,7 @@ namespace FlaxEditor.Content
|
||||
|
||||
// Auto fit actor to camera
|
||||
float targetSize = 30.0f;
|
||||
BoundingBox bounds;
|
||||
Editor.GetActorEditorBox(_preview.Instance, out bounds);
|
||||
Editor.GetActorEditorBox(_preview.Instance, out var bounds);
|
||||
float maxSize = Mathf.Max(0.001f, bounds.Size.MaxValue);
|
||||
_preview.Instance.Scale = new Vector3(targetSize / maxSize);
|
||||
_preview.Instance.Position = Vector3.Zero;
|
||||
@@ -175,6 +174,7 @@ namespace FlaxEditor.Content
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.RemoveChildren();
|
||||
_preview.Prefab = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
@@ -295,7 +295,8 @@ namespace FlaxEditor.Content
|
||||
StartRenaming();
|
||||
return true;
|
||||
case KeyboardKeys.Delete:
|
||||
Editor.Instance.Windows.ContentWin.Delete(Folder);
|
||||
if (Folder.Exists)
|
||||
Editor.Instance.Windows.ContentWin.Delete(Folder);
|
||||
return true;
|
||||
}
|
||||
if (RootWindow.GetKey(KeyboardKeys.Control))
|
||||
@@ -303,7 +304,8 @@ namespace FlaxEditor.Content
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.D:
|
||||
Editor.Instance.Windows.ContentWin.Duplicate(Folder);
|
||||
if (Folder.Exists)
|
||||
Editor.Instance.Windows.ContentWin.Duplicate(Folder);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors
|
||||
// Space before word starting with uppercase letter
|
||||
if (char.IsUpper(c) && i > 0)
|
||||
{
|
||||
if (i + 2 < length && !char.IsUpper(name[i + 1]) && !char.IsUpper(name[i + 2]))
|
||||
if (i + 1 < length && !char.IsUpper(name[i + 1]))
|
||||
sb.Append(' ');
|
||||
}
|
||||
// Space instead of underscore
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
upperRightCell.AddChild(new Label
|
||||
{
|
||||
Height = labelsHeight,
|
||||
Text = layerNames[layerIndex],
|
||||
Text = layerNames[layerNames.Length - layerIndex - 1],
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
});
|
||||
bottomLeftCell.AddChild(new Label
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace FlaxEditor
|
||||
if (loadingPreview != null)
|
||||
{
|
||||
// Link it to the prefab preview to see it in the editor
|
||||
loadingPreview.customControlLinked = control.Control;
|
||||
loadingPreview.customControlLinked = control;
|
||||
return loadingPreview;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -355,12 +355,10 @@ namespace FlaxEditor.GUI
|
||||
_mousePos = location;
|
||||
|
||||
// Check if start drag drop
|
||||
if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f)
|
||||
if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f && IconRect.Contains(_mouseDownPos))
|
||||
{
|
||||
// Clear flag
|
||||
_isMouseDown = false;
|
||||
|
||||
// Do the drag
|
||||
_isMouseDown = false;
|
||||
DoDrag();
|
||||
}
|
||||
|
||||
@@ -370,35 +368,35 @@ namespace FlaxEditor.GUI
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Vector2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
if (button == MouseButton.Left && _isMouseDown)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
}
|
||||
|
||||
// Buttons logic
|
||||
if (Button1Rect.Contains(location))
|
||||
{
|
||||
// Show asset picker popup
|
||||
Focus();
|
||||
AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem =>
|
||||
// Buttons logic
|
||||
if (Button1Rect.Contains(location))
|
||||
{
|
||||
SelectedItem = assetItem;
|
||||
RootWindow.Focus();
|
||||
// Show asset picker popup
|
||||
Focus();
|
||||
});
|
||||
}
|
||||
else if (_selected != null || _selectedItem != null)
|
||||
{
|
||||
if (Button2Rect.Contains(location) && _selectedItem != null)
|
||||
{
|
||||
// Select asset
|
||||
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
|
||||
AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem =>
|
||||
{
|
||||
SelectedItem = assetItem;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
}
|
||||
else if (Button3Rect.Contains(location))
|
||||
else if (_selected != null || _selectedItem != null)
|
||||
{
|
||||
// Deselect asset
|
||||
Focus();
|
||||
SelectedItem = null;
|
||||
if (Button2Rect.Contains(location) && _selectedItem != null)
|
||||
{
|
||||
// Select asset
|
||||
Editor.Instance.Windows.ContentWin.Select(_selectedItem);
|
||||
}
|
||||
else if (Button3Rect.Contains(location))
|
||||
{
|
||||
// Deselect asset
|
||||
Focus();
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,8 +407,7 @@ namespace FlaxEditor.GUI
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Vector2 location, MouseButton button)
|
||||
{
|
||||
// Set flag for dragging asset
|
||||
if (button == MouseButton.Left && IconRect.Contains(location))
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
_isMouseDown = true;
|
||||
_mouseDownPos = location;
|
||||
|
||||
@@ -307,6 +307,14 @@ namespace FlaxEditor.GUI.Docking
|
||||
_dockedTo?.SelectTab(this, autoFocus);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Brings the window to the front of the Z order.
|
||||
/// </summary>
|
||||
public void BringToFront()
|
||||
{
|
||||
_dockedTo?.RootWindow?.BringToFront();
|
||||
}
|
||||
|
||||
internal void OnUnlinkInternal()
|
||||
{
|
||||
OnUnlink();
|
||||
@@ -412,6 +420,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
base.Focus();
|
||||
|
||||
SelectTab();
|
||||
BringToFront();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -49,6 +49,11 @@ namespace FlaxEditor.GUI.Input
|
||||
/// </summary>
|
||||
protected T _startSlideValue;
|
||||
|
||||
/// <summary>
|
||||
/// The text cached on editing start. Used to compare with the end result to detect changes.
|
||||
/// </summary>
|
||||
protected string _startEditText;
|
||||
|
||||
private Vector2 _startSlideLocation;
|
||||
|
||||
/// <summary>
|
||||
@@ -257,11 +262,23 @@ namespace FlaxEditor.GUI.Input
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnEditBegin()
|
||||
{
|
||||
base.OnEditBegin();
|
||||
|
||||
_startEditText = _text;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnEditEnd()
|
||||
{
|
||||
// Update value
|
||||
TryGetValue();
|
||||
if (_startEditText != _text)
|
||||
{
|
||||
// Update value
|
||||
TryGetValue();
|
||||
}
|
||||
_startEditText = null;
|
||||
|
||||
base.OnEditEnd();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
/// The Timeline track that contains a header and custom timeline events/media.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
[HideInEditor]
|
||||
public class Track : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
/// <summary>
|
||||
/// The default node offset on Y axis.
|
||||
/// </summary>
|
||||
public const float DefaultNodeOffsetY = 1;
|
||||
public const float DefaultNodeOffsetY = 0;
|
||||
|
||||
private Tree _tree;
|
||||
|
||||
@@ -539,7 +539,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
{
|
||||
if (new Rectangle(_headerRect.X, _headerRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
||||
_dragOverMode = DragItemPositioning.Above;
|
||||
else if (IsCollapsed && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
||||
else if ((IsCollapsed || !HasAnyVisibleChild) && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location))
|
||||
_dragOverMode = DragItemPositioning.Below;
|
||||
else
|
||||
_dragOverMode = DragItemPositioning.At;
|
||||
|
||||
@@ -196,12 +196,10 @@ namespace FlaxEditor.Gizmo
|
||||
private void UpdateTranslateScale()
|
||||
{
|
||||
bool isScaling = _activeMode == Mode.Scale;
|
||||
|
||||
Vector3 delta = Vector3.Zero;
|
||||
Ray ray = Owner.MouseRay;
|
||||
|
||||
Matrix invRotationMatrix;
|
||||
Matrix.Invert(ref _rotationMatrix, out invRotationMatrix);
|
||||
Matrix.Invert(ref _rotationMatrix, out var invRotationMatrix);
|
||||
ray.Position = Vector3.Transform(ray.Position, invRotationMatrix);
|
||||
Vector3.TransformNormal(ref ray.Direction, ref invRotationMatrix, out ray.Direction);
|
||||
|
||||
@@ -211,9 +209,7 @@ namespace FlaxEditor.Gizmo
|
||||
case Axis.X:
|
||||
{
|
||||
var plane = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z);
|
||||
|
||||
float intersection;
|
||||
if (ray.Intersects(ref plane, out intersection))
|
||||
if (ray.Intersects(ref plane, out float intersection))
|
||||
{
|
||||
_intersectPosition = ray.Position + ray.Direction * intersection;
|
||||
if (_lastIntersectionPosition != Vector3.Zero)
|
||||
@@ -222,18 +218,14 @@ namespace FlaxEditor.Gizmo
|
||||
? new Vector3(_tDelta.X, 0, 0)
|
||||
: new Vector3(_tDelta.X, _tDelta.Y, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Axis.Z:
|
||||
case Axis.YZ:
|
||||
case Axis.Y:
|
||||
{
|
||||
var plane = new Plane(Vector3.Left, Vector3.Transform(Position, invRotationMatrix).X);
|
||||
|
||||
float intersection;
|
||||
if (ray.Intersects(ref plane, out intersection))
|
||||
if (ray.Intersects(ref plane, out float intersection))
|
||||
{
|
||||
_intersectPosition = ray.Position + ray.Direction * intersection;
|
||||
if (_lastIntersectionPosition != Vector3.Zero)
|
||||
@@ -251,41 +243,31 @@ namespace FlaxEditor.Gizmo
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Axis.ZX:
|
||||
{
|
||||
var plane = new Plane(Vector3.Down, Vector3.Transform(Position, invRotationMatrix).Y);
|
||||
|
||||
float intersection;
|
||||
if (ray.Intersects(ref plane, out intersection))
|
||||
if (ray.Intersects(ref plane, out float intersection))
|
||||
{
|
||||
_intersectPosition = ray.Position + ray.Direction * intersection;
|
||||
if (_lastIntersectionPosition != Vector3.Zero)
|
||||
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
||||
delta = new Vector3(_tDelta.X, 0, _tDelta.Z);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Axis.Center:
|
||||
{
|
||||
Vector3 gizmoToView = Position - Owner.ViewPosition;
|
||||
var gizmoToView = Position - Owner.ViewPosition;
|
||||
var plane = new Plane(-Vector3.Normalize(gizmoToView), gizmoToView.Length);
|
||||
|
||||
float intersection;
|
||||
if (ray.Intersects(ref plane, out intersection))
|
||||
if (ray.Intersects(ref plane, out float intersection))
|
||||
{
|
||||
_intersectPosition = ray.Position + ray.Direction * intersection;
|
||||
if (_lastIntersectionPosition != Vector3.Zero)
|
||||
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
||||
}
|
||||
|
||||
delta = _tDelta;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -299,14 +281,11 @@ namespace FlaxEditor.Gizmo
|
||||
if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping)
|
||||
{
|
||||
float snapValue = isScaling ? ScaleSnapValue : TranslationSnapValue;
|
||||
|
||||
_translationScaleSnapDelta += delta;
|
||||
|
||||
delta = new Vector3(
|
||||
(int)(_translationScaleSnapDelta.X / snapValue) * snapValue,
|
||||
(int)(_translationScaleSnapDelta.Y / snapValue) * snapValue,
|
||||
(int)(_translationScaleSnapDelta.Z / snapValue) * snapValue);
|
||||
|
||||
_translationScaleSnapDelta -= delta;
|
||||
}
|
||||
|
||||
@@ -318,7 +297,30 @@ namespace FlaxEditor.Gizmo
|
||||
}
|
||||
else if (_activeMode == Mode.Scale)
|
||||
{
|
||||
// Apply scale
|
||||
// Scale
|
||||
if (_activeTransformSpace == TransformSpace.World && _activeAxis != Axis.Center)
|
||||
{
|
||||
var deltaLocal = delta;
|
||||
Quaternion orientation = GetSelectedObject(0).Orientation;
|
||||
delta = Vector3.Transform(delta, orientation);
|
||||
|
||||
// Fix axis sign of delta movement for rotated object in some cases (eg. rotated object by 90 deg on Y axis and scale in world space with Red/X axis)
|
||||
switch (_activeAxis)
|
||||
{
|
||||
case Axis.X:
|
||||
if (deltaLocal.X < 0)
|
||||
delta *= -1;
|
||||
break;
|
||||
case Axis.Y:
|
||||
if (deltaLocal.Y < 0)
|
||||
delta *= -1;
|
||||
break;
|
||||
case Axis.Z:
|
||||
if (deltaLocal.Z < 0)
|
||||
delta *= -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_scaleDelta = delta;
|
||||
}
|
||||
}
|
||||
@@ -382,7 +384,7 @@ namespace FlaxEditor.Gizmo
|
||||
// Snap to ground
|
||||
if (_activeAxis == Axis.None && SelectionCount != 0 && Owner.SnapToGround)
|
||||
{
|
||||
if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, int.MaxValue, false))
|
||||
if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, uint.MaxValue, false))
|
||||
{
|
||||
StartTransforming();
|
||||
var translationDelta = hit.Point - Position;
|
||||
@@ -408,7 +410,6 @@ namespace FlaxEditor.Gizmo
|
||||
case Mode.Translate:
|
||||
UpdateTranslateScale();
|
||||
break;
|
||||
|
||||
case Mode.Rotate:
|
||||
UpdateRotate(dt);
|
||||
break;
|
||||
@@ -437,7 +438,7 @@ namespace FlaxEditor.Gizmo
|
||||
translationDelta = _translationDelta;
|
||||
_translationDelta = Vector3.Zero;
|
||||
|
||||
// Prevent from moving objects too far away, like to different galaxy or sth
|
||||
// Prevent from moving objects too far away, like to a different galaxy or sth
|
||||
Vector3 prevMoveDelta = _accMoveDelta;
|
||||
_accMoveDelta += _translationDelta;
|
||||
if (_accMoveDelta.Length > Owner.ViewFarPlane * 0.7f)
|
||||
|
||||
@@ -726,7 +726,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 30,
|
||||
Title = "DDX",
|
||||
Description = "Returns the partial derivative of the specified value with respect to the screen-space x-coordinate.",
|
||||
Description = "Returns the partial derivative of the specified value with respect to the screen-space x-coordinate",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Vector2(90, 25),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
@@ -742,7 +742,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 31,
|
||||
Title = "DDY",
|
||||
Description = "Returns the partial derivative of the specified value with respect to the screen-space y-coordinate.",
|
||||
Description = "Returns the partial derivative of the specified value with respect to the screen-space y-coordinate",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Vector2(90, 25),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
@@ -754,6 +754,68 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 32,
|
||||
Title = "Sign",
|
||||
Description = "Returns -1 if value is less than zero; 0 if value equals zero; and 1 if value is greater than zero",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Vector2(90, 25),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
IndependentBoxes = new[] { 0 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 33,
|
||||
Title = "Any",
|
||||
Description = "True if any components of value are non-zero; otherwise, false",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Vector2(90, 25),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
IndependentBoxes = new[] { 0 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 34,
|
||||
Title = "All",
|
||||
Description = "Determines if all components of the specified value are non-zero",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Vector2(90, 25),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
IndependentBoxes = new[] { 0 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 35,
|
||||
Title = "Black Body",
|
||||
Description = "Simulates black body radiation via a given temperature in kelvin",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Vector2(120, 25),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
0.0f,
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Temp", true, typeof(float), 0, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector3), 1),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ struct GeometryLookup
|
||||
static bool Search(Actor* actor, GeometryLookup& lookup)
|
||||
{
|
||||
// Early out if object is not intersecting with the foliage brush bounds
|
||||
if (!actor->GetBox().Intersects(lookup.Brush))
|
||||
if (!actor->GetIsActive() || !actor->GetBox().Intersects(lookup.Brush))
|
||||
return true;
|
||||
|
||||
const auto brush = lookup.Brush;
|
||||
|
||||
@@ -745,6 +745,59 @@ bool EditorUtilities::GenerateCertificate(const String& name, const String& outp
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorUtilities::IsInvalidPathChar(Char c)
|
||||
{
|
||||
char illegalChars[] =
|
||||
{
|
||||
'?',
|
||||
'\\',
|
||||
'/',
|
||||
'\"',
|
||||
'<',
|
||||
'>',
|
||||
'|',
|
||||
':',
|
||||
'*',
|
||||
'\u0001',
|
||||
'\u0002',
|
||||
'\u0003',
|
||||
'\u0004',
|
||||
'\u0005',
|
||||
'\u0006',
|
||||
'\a',
|
||||
'\b',
|
||||
'\t',
|
||||
'\n',
|
||||
'\v',
|
||||
'\f',
|
||||
'\r',
|
||||
'\u000E',
|
||||
'\u000F',
|
||||
'\u0010',
|
||||
'\u0011',
|
||||
'\u0012',
|
||||
'\u0013',
|
||||
'\u0014',
|
||||
'\u0015',
|
||||
'\u0016',
|
||||
'\u0017',
|
||||
'\u0018',
|
||||
'\u0019',
|
||||
'\u001A',
|
||||
'\u001B',
|
||||
'\u001C',
|
||||
'\u001D',
|
||||
'\u001E',
|
||||
'\u001F'
|
||||
};
|
||||
for (auto i : illegalChars)
|
||||
{
|
||||
if (c == i)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorUtilities::ReplaceInFiles(const String& folderPath, const Char* searchPattern, DirectorySearchOption searchOption, const String& findWhat, const String& replaceWith)
|
||||
{
|
||||
Array<String> files;
|
||||
|
||||
@@ -42,6 +42,13 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified path character is invalid.
|
||||
/// </summary>
|
||||
/// <param name="c">The path character.</param>
|
||||
/// <returns><c>true</c> if the given character cannot be used as a path because it is illegal character; otherwise, <c>false</c>.</returns>
|
||||
static bool IsInvalidPathChar(Char c);
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the given text with other one in the files.
|
||||
/// </summary>
|
||||
|
||||
@@ -193,6 +193,7 @@ namespace FlaxEditor.Utilities
|
||||
{
|
||||
case 'x':
|
||||
case 'X':
|
||||
{
|
||||
// Hexadecimal value
|
||||
i++;
|
||||
token.Clear();
|
||||
@@ -200,22 +201,35 @@ namespace FlaxEditor.Utilities
|
||||
throw new ParsingException("invalid hexadecimal number");
|
||||
while (i + 1 < text.Length && StringUtils.IsHexDigit(text[i + 1]))
|
||||
{
|
||||
i++;
|
||||
token.Append(text[i]);
|
||||
token.Append(text[++i]);
|
||||
}
|
||||
var value = ulong.Parse(token.ToString(), NumberStyles.HexNumber);
|
||||
token.Clear();
|
||||
token.Append(value.ToString());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Decimal value
|
||||
while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Number)
|
||||
{
|
||||
i++;
|
||||
token.Append(text[i]);
|
||||
token.Append(text[++i]);
|
||||
}
|
||||
|
||||
// Exponential notation
|
||||
if (i + 2 < text.Length && (text[i + 1] == 'e' || text[i + 1] == 'E'))
|
||||
{
|
||||
token.Append(text[++i]);
|
||||
if (text[i + 1] == '-' || text[i + 1] == '+')
|
||||
token.Append(text[++i]);
|
||||
while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Number)
|
||||
{
|
||||
token.Append(text[++i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Discard solo '-'
|
||||
|
||||
@@ -533,6 +533,8 @@ namespace FlaxEditor.Utilities
|
||||
break;
|
||||
case VariantType.Enum:
|
||||
case VariantType.Structure:
|
||||
case VariantType.ManagedObject:
|
||||
case VariantType.Typename:
|
||||
stream.Write(int.MaxValue);
|
||||
stream.WriteStrAnsi(type.FullName, 77);
|
||||
break;
|
||||
@@ -762,7 +764,7 @@ namespace FlaxEditor.Utilities
|
||||
data[i] = (byte)(c ^ 77);
|
||||
}
|
||||
var typeName = System.Text.Encoding.ASCII.GetString(data);
|
||||
return TypeUtils.GetType(typeName).Type;
|
||||
return TypeUtils.GetManagedType(typeName);
|
||||
}
|
||||
if (typeNameLength > 0)
|
||||
{
|
||||
@@ -774,7 +776,7 @@ namespace FlaxEditor.Utilities
|
||||
data[i] = (char)(c ^ 77);
|
||||
}
|
||||
var typeName = new string(data);
|
||||
return TypeUtils.GetType(typeName).Type;
|
||||
return TypeUtils.GetManagedType(typeName);
|
||||
}
|
||||
switch (variantType)
|
||||
{
|
||||
@@ -827,7 +829,7 @@ namespace FlaxEditor.Utilities
|
||||
data[i] = (byte)(c ^ 77);
|
||||
}
|
||||
var typeName = System.Text.Encoding.ASCII.GetString(data);
|
||||
type = TypeUtils.GetType(typeName).Type;
|
||||
type = TypeUtils.GetManagedType(typeName);
|
||||
}
|
||||
else if (typeNameLength > 0)
|
||||
{
|
||||
@@ -839,7 +841,7 @@ namespace FlaxEditor.Utilities
|
||||
data[i] = (char)(c ^ 77);
|
||||
}
|
||||
var typeName = new string(data);
|
||||
type = TypeUtils.GetType(typeName).Type;
|
||||
type = TypeUtils.GetManagedType(typeName);
|
||||
}
|
||||
switch (variantType)
|
||||
{
|
||||
|
||||
@@ -485,6 +485,23 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Selected UI controls outline
|
||||
for (var i = 0; i < _window.Selection.Count; i++)
|
||||
{
|
||||
if (_window.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null)
|
||||
{
|
||||
var control = controlActor.Control;
|
||||
var bounds = Rectangle.FromPoints(control.PointToParent(this, Vector2.Zero), control.PointToParent(this, control.Size));
|
||||
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnLeftMouseButtonUp()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
@@ -19,7 +18,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
|
||||
private Prefab _prefab;
|
||||
private Actor _instance;
|
||||
internal Control customControlLinked;
|
||||
internal UIControl customControlLinked;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the prefab asset to preview.
|
||||
@@ -29,39 +28,37 @@ namespace FlaxEditor.Viewport.Previews
|
||||
get => _prefab;
|
||||
set
|
||||
{
|
||||
if (_prefab != value)
|
||||
if (_prefab == value)
|
||||
return;
|
||||
|
||||
// Unset and cleanup spawned instance
|
||||
if (_instance)
|
||||
{
|
||||
if (_instance)
|
||||
var instance = _instance;
|
||||
Instance = null;
|
||||
Object.Destroy(instance);
|
||||
}
|
||||
|
||||
_prefab = value;
|
||||
|
||||
if (_prefab)
|
||||
{
|
||||
// Load prefab
|
||||
_prefab.WaitForLoaded();
|
||||
|
||||
// Spawn prefab
|
||||
var prevPreview = LoadingPreview;
|
||||
LoadingPreview = this;
|
||||
var instance = PrefabManager.SpawnPrefab(_prefab, null);
|
||||
LoadingPreview = prevPreview;
|
||||
if (instance == null)
|
||||
{
|
||||
if (customControlLinked != null)
|
||||
{
|
||||
customControlLinked.Parent = null;
|
||||
customControlLinked = null;
|
||||
}
|
||||
Task.RemoveCustomActor(_instance);
|
||||
Object.Destroy(_instance);
|
||||
_prefab = null;
|
||||
throw new FlaxException("Failed to spawn a prefab for the preview.");
|
||||
}
|
||||
|
||||
_prefab = value;
|
||||
|
||||
if (_prefab)
|
||||
{
|
||||
_prefab.WaitForLoaded(); // TODO: use lazy prefab spawning to reduce stalls
|
||||
|
||||
var prevPreview = LoadingPreview;
|
||||
LoadingPreview = this;
|
||||
|
||||
_instance = PrefabManager.SpawnPrefab(_prefab, null);
|
||||
|
||||
LoadingPreview = prevPreview;
|
||||
|
||||
if (_instance == null)
|
||||
{
|
||||
_prefab = null;
|
||||
throw new FlaxException("Failed to spawn a prefab for the preview.");
|
||||
}
|
||||
Task.AddCustomActor(_instance);
|
||||
}
|
||||
// Set instance
|
||||
Instance = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,7 +69,47 @@ namespace FlaxEditor.Viewport.Previews
|
||||
public Actor Instance
|
||||
{
|
||||
get => _instance;
|
||||
internal set => _instance = value;
|
||||
internal set
|
||||
{
|
||||
if (_instance == value)
|
||||
return;
|
||||
|
||||
if (_instance)
|
||||
{
|
||||
// Unlink UI control
|
||||
if (customControlLinked)
|
||||
{
|
||||
if (customControlLinked.Control?.Parent == this)
|
||||
customControlLinked.Control.Parent = null;
|
||||
customControlLinked = null;
|
||||
}
|
||||
|
||||
// Remove for the preview
|
||||
Task.RemoveCustomActor(_instance);
|
||||
}
|
||||
|
||||
_instance = value;
|
||||
|
||||
if (_instance)
|
||||
{
|
||||
// Add to the preview
|
||||
Task.AddCustomActor(_instance);
|
||||
|
||||
// Link UI canvases to the preview
|
||||
LinkCanvas(_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LinkCanvas(Actor actor)
|
||||
{
|
||||
if (actor is UICanvas uiCanvas)
|
||||
uiCanvas.EditorOverride(Task, this);
|
||||
var children = actor.ChildrenCount;
|
||||
for (int i = 0; i < children; i++)
|
||||
{
|
||||
LinkCanvas(actor.GetChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -87,7 +124,6 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Cleanup
|
||||
Prefab = null;
|
||||
|
||||
base.OnDestroy();
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
bool hasSthSelected = Selection.Count > 0;
|
||||
bool isSingleActorSelected = Selection.Count == 1 && Selection[0] is ActorNode;
|
||||
bool isRootSelected = isSingleActorSelected && Selection[0] == Graph.Main;
|
||||
bool hasPrefabLink = isSingleActorSelected && (Selection[0] as ActorNode).HasPrefabLink;
|
||||
|
||||
// Create popup
|
||||
|
||||
@@ -97,7 +98,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
b.Enabled = hasSthSelected && !isRootSelected;
|
||||
|
||||
b = contextMenu.AddButton("Set Root", SetRoot);
|
||||
b.Enabled = isSingleActorSelected && !isRootSelected;
|
||||
b.Enabled = isSingleActorSelected && !isRootSelected && hasPrefabLink;
|
||||
|
||||
// Prefab options
|
||||
|
||||
@@ -108,8 +109,6 @@ namespace FlaxEditor.Windows.Assets
|
||||
(Selection[0] as ActorNode).CanCreatePrefab &&
|
||||
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
||||
|
||||
bool hasPrefabLink = isSingleActorSelected && (Selection[0] as ActorNode).HasPrefabLink;
|
||||
|
||||
b = contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab);
|
||||
b.Enabled = hasPrefabLink;
|
||||
|
||||
|
||||
@@ -284,6 +284,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
// Simply update changes
|
||||
Editor.Prefabs.ApplyAll(_viewport.Instance);
|
||||
|
||||
// Refresh properties panel to sync new prefab default values
|
||||
_propertiesEditor.BuildLayout();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
@@ -396,7 +396,7 @@ namespace FlaxEditor.Windows
|
||||
/// <param name="item">The item to delete.</param>
|
||||
public void Delete(ContentItem item)
|
||||
{
|
||||
Delete(new List<ContentItem> { item });
|
||||
Delete(new List<ContentItem>(1) { item });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -405,36 +405,18 @@ namespace FlaxEditor.Windows
|
||||
/// <param name="items">The items to delete.</param>
|
||||
public void Delete(List<ContentItem> items)
|
||||
{
|
||||
if (items.Count == 0) return;
|
||||
|
||||
// TODO: remove items that depend on different items in the list: use wants to remove `folderA` and `folderA/asset.x`, we should just remove `folderA`
|
||||
var toDelete = new List<ContentItem>(items);
|
||||
|
||||
string msg = toDelete.Count == 1 ?
|
||||
string.Format("Are you sure to delete \'{0}\'?\nThis action cannot be undone. Files will be deleted permanently.", items[0].Path)
|
||||
: string.Format("Are you sure to delete {0} selected items?\nThis action cannot be undone. Files will be deleted permanently.", items.Count);
|
||||
|
||||
// Ask user
|
||||
if (toDelete.Count == 1)
|
||||
{
|
||||
// Single item
|
||||
if (MessageBox.Show(string.Format("Are you sure to delete \'{0}\'?\nThis action cannot be undone. Files will be deleted permanently.", items[0].Path),
|
||||
"Delete asset(s)",
|
||||
MessageBoxButtons.OKCancel,
|
||||
MessageBoxIcon.Question)
|
||||
!= DialogResult.OK)
|
||||
{
|
||||
// Break
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Many items
|
||||
if (MessageBox.Show(string.Format("Are you sure to delete {0} selected items?\nThis action cannot be undone. Files will be deleted permanently.", items.Count),
|
||||
"Delete asset(s)",
|
||||
MessageBoxButtons.OKCancel,
|
||||
MessageBoxIcon.Question)
|
||||
!= DialogResult.OK)
|
||||
{
|
||||
// Break
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (MessageBox.Show(msg, "Delete asset(s)", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK)
|
||||
return;
|
||||
|
||||
// Clear navigation
|
||||
// TODO: just remove invalid locations from the history (those are removed)
|
||||
|
||||
@@ -294,7 +294,9 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (Editor.Instance.SceneEditing.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null)
|
||||
{
|
||||
Render2D.DrawRectangle(controlActor.Control.Bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
|
||||
var control = controlActor.Control;
|
||||
var bounds = Rectangle.FromPoints(control.PointToParent(_viewport, Vector2.Zero), control.PointToParent(_viewport, control.Size));
|
||||
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("That's what she said"),
|
||||
TEXT("Compiling Shaders (93,788)"),
|
||||
TEXT("Hi There"),
|
||||
TEXT("BAGUETTE"),
|
||||
};
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
|
||||
@@ -74,7 +74,8 @@ protected:
|
||||
/// <summary>
|
||||
/// Generic type of Json-format asset. It provides the managed representation of this resource data so it can be accessed via C# API.
|
||||
/// </summary>
|
||||
API_CLASS(NoSpawn) class JsonAsset : public JsonAssetBase
|
||||
/// <seealso cref="JsonAssetBase" />
|
||||
API_CLASS(NoSpawn) class FLAXENGINE_API JsonAsset : public JsonAssetBase
|
||||
{
|
||||
DECLARE_ASSET_HEADER(JsonAsset);
|
||||
private:
|
||||
|
||||
@@ -178,7 +178,7 @@ void CreateAssetContext::ApplyChanges()
|
||||
// Move file
|
||||
if (FileSystem::MoveFile(TargetAssetPath, OutputPath, true))
|
||||
{
|
||||
LOG(Warning, "Cannot move imported file to the destination path.");
|
||||
LOG(Warning, "Cannot move imported file {0} to the destination path {1}.", OutputPath, TargetAssetPath);
|
||||
_applyChangesResult = CreateAssetResult::CannotSaveFile;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Config/Settings.h"
|
||||
#include "Engine/Serialization/Json.h"
|
||||
|
||||
/// <summary>
|
||||
/// Layers and objects tags settings.
|
||||
|
||||
@@ -1221,7 +1221,7 @@ namespace FlaxEngine
|
||||
float dot = Mathf.Clamp(Dot(from.Normalized, to.Normalized), -1F, 1F);
|
||||
if (Mathf.Abs(dot) > (1F - Mathf.Epsilon))
|
||||
return dot > 0F ? 0F : 180F;
|
||||
return Mathf.Acos(dot) * Mathf.DegreesToRadians;
|
||||
return Mathf.Acos(dot) * Mathf.RadiansToDegrees;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Animations/AnimationUtils.h"
|
||||
#include "Engine/Profiler/Profiler.h"
|
||||
#include "Engine/Debug/DebugLog.h"
|
||||
|
||||
// Debug draw service configuration
|
||||
#define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024)
|
||||
@@ -674,7 +675,11 @@ void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color&
|
||||
|
||||
void DebugDraw::DrawLines(const Span<Vector3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
||||
{
|
||||
ASSERT(lines.Length() % 2 == 0);
|
||||
if (lines.Length() % 2 != 0)
|
||||
{
|
||||
DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create draw call entry
|
||||
DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration };
|
||||
@@ -682,10 +687,12 @@ void DebugDraw::DrawLines(const Span<Vector3>& lines, const Matrix& transform, c
|
||||
// Add lines
|
||||
const Vector3* p = lines.Get();
|
||||
Array<DebugLine>* list;
|
||||
if (depthTest)
|
||||
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultLines : &DebugDrawDepthTest.OneFrameLines;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultLines : &DebugDrawDefault.OneFrameLines;
|
||||
|
||||
list->EnsureCapacity(list->Count() + lines.Length());
|
||||
for (int32 i = 0; i < lines.Length(); i += 2)
|
||||
{
|
||||
|
||||
@@ -283,11 +283,10 @@ Viewport SceneRenderTask::GetViewport() const
|
||||
|
||||
GPUTextureView* SceneRenderTask::GetOutputView() const
|
||||
{
|
||||
if (Output)
|
||||
if (Output && Output->IsAllocated())
|
||||
return Output->View();
|
||||
if (SwapChain)
|
||||
return SwapChain->GetBackBufferView();
|
||||
CRASH;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -458,43 +458,13 @@ protected:
|
||||
// [ThreadPoolTask]
|
||||
bool Run() override
|
||||
{
|
||||
// Check resources
|
||||
auto texture = _texture.Get();
|
||||
if (texture == nullptr || _staging == nullptr || _data == nullptr)
|
||||
{
|
||||
LOG(Warning, "Cannot download texture data. Missing objects.");
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto arraySize = texture->ArraySize();
|
||||
const auto mipLevels = texture->MipLevels();
|
||||
|
||||
// Get all mip maps for each array slice
|
||||
auto& rawResultData = _data->Items;
|
||||
rawResultData.Resize(arraySize, false);
|
||||
for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
|
||||
{
|
||||
auto& arraySlice = rawResultData[arrayIndex];
|
||||
arraySlice.Mips.Resize(mipLevels);
|
||||
|
||||
for (int32 mipMapIndex = 0; mipMapIndex < mipLevels; mipMapIndex++)
|
||||
{
|
||||
auto& mip = arraySlice.Mips[mipMapIndex];
|
||||
const int32 mipWidth = _data->Width >> mipMapIndex;
|
||||
const int32 mipHeight = _data->Height >> mipMapIndex;
|
||||
uint32 mipRowPitch, mipSlicePitch;
|
||||
RenderTools::ComputePitch(_data->Format, mipWidth, mipHeight, mipRowPitch, mipSlicePitch);
|
||||
|
||||
// Gather data
|
||||
if (_staging->GetData(arrayIndex, mipMapIndex, mip, mipRowPitch))
|
||||
{
|
||||
LOG(Warning, "Staging resource of \'{0}\' get data failed.", texture->ToString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return _staging->DownloadData(*_data);
|
||||
}
|
||||
|
||||
void OnEnd() override
|
||||
@@ -508,6 +478,57 @@ protected:
|
||||
|
||||
bool GPUTexture::DownloadData(TextureData& result)
|
||||
{
|
||||
// Skip for empty ones
|
||||
if (MipLevels() == 0)
|
||||
{
|
||||
LOG(Warning, "Cannot download GPU texture data from an empty texture.");
|
||||
return true;
|
||||
}
|
||||
if (Depth() != 1)
|
||||
{
|
||||
MISSING_CODE("support volume texture data downloading.");
|
||||
}
|
||||
|
||||
// Use faster path for staging resources
|
||||
if (IsStaging())
|
||||
{
|
||||
const auto arraySize = ArraySize();
|
||||
const auto mipLevels = MipLevels();
|
||||
|
||||
// Set texture info
|
||||
result.Width = Width();
|
||||
result.Height = Height();
|
||||
result.Depth = Depth();
|
||||
result.Format = Format();
|
||||
|
||||
// Get all mip maps for each array slice
|
||||
auto& rawResultData = result.Items;
|
||||
rawResultData.Resize(arraySize, false);
|
||||
for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
|
||||
{
|
||||
auto& arraySlice = rawResultData[arrayIndex];
|
||||
arraySlice.Mips.Resize(mipLevels);
|
||||
|
||||
for (int32 mipMapIndex = 0; mipMapIndex < mipLevels; mipMapIndex++)
|
||||
{
|
||||
auto& mip = arraySlice.Mips[mipMapIndex];
|
||||
const int32 mipWidth = result.Width >> mipMapIndex;
|
||||
const int32 mipHeight = result.Height >> mipMapIndex;
|
||||
uint32 mipRowPitch, mipSlicePitch;
|
||||
RenderTools::ComputePitch(result.Format, mipWidth, mipHeight, mipRowPitch, mipSlicePitch);
|
||||
|
||||
// Gather data
|
||||
if (GetData(arrayIndex, mipMapIndex, mip, mipRowPitch))
|
||||
{
|
||||
LOG(Warning, "Staging resource of \'{0}\' get data failed.", ToString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto name = ToString();
|
||||
|
||||
// Ensure not running on main thread - we support DownloadData from textures only on a worker threads (Thread Pool Workers or Content Loaders)
|
||||
@@ -538,7 +559,8 @@ bool GPUTexture::DownloadData(TextureData& result)
|
||||
|
||||
Task* GPUTexture::DownloadDataAsync(TextureData& result)
|
||||
{
|
||||
if (!IsAllocated())
|
||||
// Skip for empty ones
|
||||
if (MipLevels() == 0)
|
||||
{
|
||||
LOG(Warning, "Cannot download texture data. It has not ben created yet.");
|
||||
return nullptr;
|
||||
@@ -548,19 +570,12 @@ Task* GPUTexture::DownloadDataAsync(TextureData& result)
|
||||
MISSING_CODE("support volume texture data downloading.");
|
||||
}
|
||||
|
||||
// Set texture info
|
||||
result.Width = Width();
|
||||
result.Height = Height();
|
||||
result.Depth = Depth();
|
||||
result.Format = Format();
|
||||
|
||||
// Quicker path if texture is already readback
|
||||
if (_desc.Usage == GPUResourceUsage::StagingReadback)
|
||||
// Use faster path for staging resources
|
||||
if (IsStaging())
|
||||
{
|
||||
// Create task to copy downloaded data to TextureData container
|
||||
auto getDataTask = ::New<TextureDownloadDataTask>(this, this, result);
|
||||
ASSERT(getDataTask->HasReference(this));
|
||||
|
||||
return getDataTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -197,6 +197,7 @@ bool Mouse::Update(EventQueue& queue)
|
||||
}
|
||||
case EventType::MouseDoubleClick:
|
||||
{
|
||||
_state.MouseButtons[static_cast<int32>(e.MouseData.Button)] = true;
|
||||
break;
|
||||
}
|
||||
case EventType::MouseWheel:
|
||||
|
||||
@@ -42,6 +42,8 @@ Actor::Actor(const SpawnParams& params)
|
||||
, _staticFlags(StaticFlags::FullyStatic)
|
||||
, _localTransform(Transform::Identity)
|
||||
, _transform(Transform::Identity)
|
||||
, _sphere(BoundingSphere::Empty)
|
||||
, _box(BoundingBox::Empty)
|
||||
, HideFlags(HideFlags::None)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -445,13 +445,13 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets actor orientation in 3D space
|
||||
/// Sets actor orientation in 3D space.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set.</param>
|
||||
API_PROPERTY() void SetOrientation(const Quaternion& value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets actor scale in 3D space
|
||||
/// Gets actor scale in 3D space.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize")
|
||||
FORCE_INLINE Vector3 GetScale() const
|
||||
@@ -466,13 +466,13 @@ public:
|
||||
API_PROPERTY() void SetScale(const Vector3& value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets actor rotation matrix
|
||||
/// Gets actor rotation matrix.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize")
|
||||
Matrix GetRotation() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets actor rotation matrix
|
||||
/// Sets actor rotation matrix.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set.</param>
|
||||
API_PROPERTY() void SetRotation(const Matrix& value);
|
||||
|
||||
@@ -1005,7 +1005,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, bool autoI
|
||||
if (obj && obj->GetParent() == nullptr)
|
||||
{
|
||||
sceneObjects->At(i) = nullptr;
|
||||
LOG(Warning, "Scene object {0} {1} has missing parent object after scene load. Removing it.", obj->GetID(), obj->ToString());
|
||||
LOG(Warning, "Scene object {0} {1} has missing parent object after load. Removing it.", obj->GetID(), obj->ToString());
|
||||
obj->DeleteObject();
|
||||
}
|
||||
}
|
||||
@@ -1358,12 +1358,8 @@ Actor* Level::FindActor(const MClass* type)
|
||||
CHECK_RETURN(type, nullptr);
|
||||
Actor* result = nullptr;
|
||||
ScopeLock lock(ScenesLock);
|
||||
|
||||
for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++)
|
||||
{
|
||||
result = Scenes[i]->FindActor(type);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1372,25 +1368,57 @@ Script* Level::FindScript(const MClass* type)
|
||||
CHECK_RETURN(type, nullptr);
|
||||
Script* result = nullptr;
|
||||
ScopeLock lock(ScenesLock);
|
||||
|
||||
for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++)
|
||||
{
|
||||
result = Scenes[i]->FindScript(type);
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void GetActors(const MClass* type, Actor* actor, Array<Actor*>& result)
|
||||
{
|
||||
if (actor->GetClass()->IsSubClassOf(type))
|
||||
result.Add(actor);
|
||||
for (auto child : actor->Children)
|
||||
GetActors(type, child, result);
|
||||
}
|
||||
|
||||
void GetScripts(const MClass* type, Actor* actor, Array<Script*>& result)
|
||||
{
|
||||
for (auto script : actor->Scripts)
|
||||
if (script->GetClass()->IsSubClassOf(type))
|
||||
result.Add(script);
|
||||
for (auto child : actor->Children)
|
||||
GetScripts(type, child, result);
|
||||
}
|
||||
}
|
||||
|
||||
Array<Actor*> Level::GetActors(const MClass* type)
|
||||
{
|
||||
Array<Actor*> result;
|
||||
CHECK_RETURN(type, result);
|
||||
ScopeLock lock(ScenesLock);
|
||||
for (int32 i = 0; i < Scenes.Count(); i++)
|
||||
::GetActors(type, Scenes[i], result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Array<Script*> Level::GetScripts(const MClass* type)
|
||||
{
|
||||
Array<Script*> result;
|
||||
CHECK_RETURN(type, result);
|
||||
ScopeLock lock(ScenesLock);
|
||||
for (int32 i = 0; i < Scenes.Count(); i++)
|
||||
::GetScripts(type, Scenes[i], result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Scene* Level::FindScene(const Guid& id)
|
||||
{
|
||||
ScopeLock lock(ScenesLock);
|
||||
|
||||
for (int32 i = 0; i < Scenes.Count(); i++)
|
||||
{
|
||||
if (Scenes[i]->GetID() == id)
|
||||
return Scenes[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,5 +77,33 @@ namespace FlaxEngine
|
||||
{
|
||||
return FindActor(id) as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the scripts of the given type in all the loaded scenes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the object.</typeparam>
|
||||
/// <returns>Found scripts list.</returns>
|
||||
public static T[] GetScripts<T>() where T : Script
|
||||
{
|
||||
var scripts = GetScripts(typeof(T));
|
||||
var result = new T[scripts.Length];
|
||||
for (int i = 0; i < scripts.Length; i++)
|
||||
result[i] = scripts[i] as T;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the actors of the given type in all the loaded scenes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the object.</typeparam>
|
||||
/// <returns>Found actors list.</returns>
|
||||
public static T[] GetActors<T>() where T : Actor
|
||||
{
|
||||
var actors = GetActors(typeof(T));
|
||||
var result = new T[actors.Length];
|
||||
for (int i = 0; i < actors.Length; i++)
|
||||
result[i] = actors[i] as T;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ public:
|
||||
/// </summary>
|
||||
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||
template<typename T>
|
||||
FORCE_INLINE T* FindActor() const
|
||||
FORCE_INLINE static T* FindActor()
|
||||
{
|
||||
return (T*)FindActor(T::GetStaticClass());
|
||||
}
|
||||
@@ -372,11 +372,25 @@ public:
|
||||
/// </summary>
|
||||
/// <returns>Script instance if found, null otherwise.</returns>
|
||||
template<typename T>
|
||||
FORCE_INLINE T* FindScript() const
|
||||
FORCE_INLINE static T* FindScript()
|
||||
{
|
||||
return (T*)FindScript(T::GetStaticClass());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the actors of the given type in all the loaded scenes.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||
/// <returns>Found actors list.</returns>
|
||||
API_FUNCTION() static Array<Actor*> GetActors(const MClass* type);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the scripts of the given type in all the loaded scenes.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
|
||||
/// <returns>Found scripts list.</returns>
|
||||
API_FUNCTION() static Array<Script*> GetScripts(const MClass* type);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find scene with given ID.
|
||||
/// </summary>
|
||||
|
||||
@@ -181,6 +181,8 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid
|
||||
|
||||
// Prepare parent linkage for prefab root actor
|
||||
root->_parent = parent;
|
||||
if (parent)
|
||||
parent->Children.Add(root);
|
||||
|
||||
// Link actors hierarchy
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
@@ -226,7 +228,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid
|
||||
if (obj && obj->GetParent() == nullptr)
|
||||
{
|
||||
sceneObjects->At(i) = nullptr;
|
||||
LOG(Warning, "Scene object {0} {1} has missing parent objct after prefab spawn. Removing it.", obj->GetID(), obj->ToString());
|
||||
LOG(Warning, "Scene object {0} {1} has missing parent object after load. Removing it.", obj->GetID(), obj->ToString());
|
||||
obj->DeleteObject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace
|
||||
|
||||
scale *= 1.72531f;
|
||||
}
|
||||
return noise / weight;
|
||||
return noise / Math::Max(weight, ZeroTolerance);
|
||||
}
|
||||
|
||||
VariantType::Types GetVariantType(ParticleAttribute::ValueTypes type)
|
||||
@@ -486,7 +486,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode*
|
||||
float particleDrag = drag; \
|
||||
if (useSpriteSize) \
|
||||
particleDrag *= ((Vector2*)spriteSizePtr)->MulValues(); \
|
||||
*((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * _deltaTime) / *(float*)massPtr); \
|
||||
*((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * _deltaTime) / Math::Max(*(float*)massPtr, ZeroTolerance)); \
|
||||
velocityPtr += stride; \
|
||||
massPtr += stride; \
|
||||
spriteSizePtr += stride
|
||||
@@ -545,7 +545,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode*
|
||||
Vector3 vectorFieldUVW = Vector3::Transform(*((Vector3*)positionPtr), invFieldTransformMatrix); \
|
||||
Vector3 force = Noise3D(vectorFieldUVW + 0.5f, octavesCount, roughness); \
|
||||
force = Vector3::Transform(force, fieldTransformMatrix) * intensity; \
|
||||
*((Vector3*)velocityPtr) += force * (_deltaTime / *(float*)massPtr); \
|
||||
*((Vector3*)velocityPtr) += force * (_deltaTime / Math::Max(*(float*)massPtr, ZeroTolerance)); \
|
||||
positionPtr += stride; \
|
||||
velocityPtr += stride; \
|
||||
massPtr += stride
|
||||
@@ -1009,7 +1009,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode*
|
||||
|
||||
#define INPUTS_FETCH() \
|
||||
const Vector3 center = (Vector3)GetValue(centerBox, 2); \
|
||||
const float radius = (float)GetValue(radiusBox, 3); \
|
||||
const float radius = Math::Max((float)GetValue(radiusBox, 3), ZeroTolerance); \
|
||||
const float thickness = (float)GetValue(thicknessBox, 4); \
|
||||
const float arc = (float)GetValue(arcBox, 5) * DegreesToRadians
|
||||
#define LOGIC() \
|
||||
@@ -1017,20 +1017,20 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode*
|
||||
float sinTheta, cosTheta; \
|
||||
Math::SinCos(u.X * TWO_PI, sinTheta, cosTheta); \
|
||||
float r = Math::Saturate(thickness / radius); \
|
||||
Vector2 s1_1 = r * Vector2(cosTheta, sinTheta) + Vector2(1, 0); \
|
||||
Vector2 s1_2 = r * Vector2(-cosTheta, sinTheta) + Vector2(1, 0); \
|
||||
float w = s1_1.X / (s1_1.X + s1_2.X); \
|
||||
Vector2 s11 = r * Vector2(cosTheta, sinTheta) + Vector2(1, 0); \
|
||||
Vector2 s12 = r * Vector2(-cosTheta, sinTheta) + Vector2(1, 0); \
|
||||
float w = s11.X / (s11.X + s12.X); \
|
||||
Vector3 t; \
|
||||
float phi; \
|
||||
if (u.Y < w) \
|
||||
{ \
|
||||
phi = arc * u.Y / w; \
|
||||
t = Vector3(s1_1.X, 0, s1_1.Y); \
|
||||
t = Vector3(s11.X, 0, s11.Y); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
phi = arc * (u.Y - w) / (1.0f - w); \
|
||||
t = Vector3(s1_2.X, 0, s1_2.Y); \
|
||||
t = Vector3(s12.X, 0, s12.Y); \
|
||||
} \
|
||||
float s, c; \
|
||||
Math::SinCos(phi, c, s); \
|
||||
@@ -1262,7 +1262,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode*
|
||||
if (sign * sqrLength <= sign * totalRadius * totalRadius) \
|
||||
{ \
|
||||
float dist = Math::Sqrt(sqrLength); \
|
||||
Vector3 n = sign * dir / dist; \
|
||||
Vector3 n = sign * dir / Math::Max(dist, ZeroTolerance); \
|
||||
*(Vector3*)positionPtr = position - n * (dist - totalRadius) * sign; \
|
||||
COLLISION_LOGIC()
|
||||
|
||||
@@ -1374,7 +1374,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode*
|
||||
collision = Math::Abs(dir.Y) > halfHeight || sqrLength > cylinderRadiusT * cylinderRadiusT; \
|
||||
if (collision) \
|
||||
{ \
|
||||
float dist = Math::Sqrt(sqrLength); \
|
||||
float dist = Math::Max(Math::Sqrt(sqrLength), ZeroTolerance); \
|
||||
float distToCap = sign * (halfHeight - Math::Abs(dir.Y)); \
|
||||
float distToSide = sign * (cylinderRadiusT - dist); \
|
||||
Vector3 n = Vector3(dir.X / dist, Math::Sign(dir.Y), dir.Z / dist) * sign; \
|
||||
|
||||
@@ -284,7 +284,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node
|
||||
{
|
||||
const float age = GET_PARTICLE_ATTRIBUTE(0, float);
|
||||
const float lifetime = GET_PARTICLE_ATTRIBUTE(1, float);
|
||||
value = age / lifetime;
|
||||
value = age / Math::Max(lifetime, ZeroTolerance);
|
||||
break;
|
||||
}
|
||||
// Effect Position
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_PARTICLE_GPU_GRAPH
|
||||
|
||||
#include "ParticleEmitterGraph.GPU.h"
|
||||
|
||||
void ParticleEmitterGPUGenerator::ProcessGroupParameters(Box* box, Node* node, Value& value)
|
||||
{
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Get
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Get parameter
|
||||
const auto param = findParam((Guid)node->Values[0]);
|
||||
if (param)
|
||||
{
|
||||
switch (param->Type)
|
||||
{
|
||||
case MaterialParameterType::Bool:
|
||||
value = Value(VariantType::Bool, param->ShaderName);
|
||||
break;
|
||||
case MaterialParameterType::Integer:
|
||||
case MaterialParameterType::SceneTexture:
|
||||
value = Value(VariantType::Int, param->ShaderName);
|
||||
break;
|
||||
case MaterialParameterType::Float:
|
||||
value = Value(VariantType::Float, param->ShaderName);
|
||||
break;
|
||||
case MaterialParameterType::Vector2:
|
||||
case MaterialParameterType::Vector3:
|
||||
case MaterialParameterType::Vector4:
|
||||
case MaterialParameterType::Color:
|
||||
{
|
||||
// Set result values based on box ID
|
||||
const Value sample(box->Type.Type, param->ShaderName);
|
||||
switch (box->ID)
|
||||
{
|
||||
case 0:
|
||||
value = sample;
|
||||
break;
|
||||
case 1:
|
||||
value.Value = sample.Value + _subs[0];
|
||||
break;
|
||||
case 2:
|
||||
value.Value = sample.Value + _subs[1];
|
||||
break;
|
||||
case 3:
|
||||
value.Value = sample.Value + _subs[2];
|
||||
break;
|
||||
case 4:
|
||||
value.Value = sample.Value + _subs[3];
|
||||
break;
|
||||
default: CRASH;
|
||||
break;
|
||||
}
|
||||
value.Type = box->Type.Type;
|
||||
break;
|
||||
}
|
||||
|
||||
case MaterialParameterType::Matrix:
|
||||
{
|
||||
value = Value(box->Type.Type, String::Format(TEXT("{0}[{1}]"), param->ShaderName, box->ID));
|
||||
break;
|
||||
}
|
||||
case MaterialParameterType::ChannelMask:
|
||||
{
|
||||
const auto input = tryGetValue(node->GetBox(0), Value::Zero);
|
||||
value = writeLocal(VariantType::Float, String::Format(TEXT("dot({0}, {1})"), input.Value, param->ShaderName), node);
|
||||
break;
|
||||
}
|
||||
case MaterialParameterType::CubeTexture:
|
||||
case MaterialParameterType::Texture:
|
||||
case MaterialParameterType::GPUTextureArray:
|
||||
case MaterialParameterType::GPUTextureCube:
|
||||
case MaterialParameterType::GPUTextureVolume:
|
||||
case MaterialParameterType::GPUTexture:
|
||||
value = Value(VariantType::Object, param->ShaderName);
|
||||
break;
|
||||
default:
|
||||
CRASH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnError(node, box, String::Format(TEXT("Missing graph parameter {0}."), node->Values[0]));
|
||||
value = Value::Zero;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -177,7 +177,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node)
|
||||
" {{\n"
|
||||
" // Linear Drag\n"
|
||||
" float drag = {2} * {3}.x * {3}.y;\n"
|
||||
" {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / {1});\n"
|
||||
" {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / max({1}, PARTICLE_THRESHOLD));\n"
|
||||
" }}\n"
|
||||
), velocity.Value, mass.Value, drag.Value, spriteSize.Value);
|
||||
}
|
||||
@@ -188,7 +188,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node)
|
||||
" {{\n"
|
||||
" // Linear Drag\n"
|
||||
" float drag = {2};\n"
|
||||
" {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / {1});\n"
|
||||
" {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / max({1}, PARTICLE_THRESHOLD));\n"
|
||||
" }}\n"
|
||||
), velocity.Value, mass.Value, drag.Value);
|
||||
}
|
||||
@@ -219,7 +219,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node)
|
||||
" float3 vectorFieldUVW = mul(invFieldTransformMatrix, float4({0}, 1.0f)).xyz;\n"
|
||||
" float3 force = Noise3D(vectorFieldUVW + 0.5f, {8}, {6});\n"
|
||||
" force = mul(fieldTransformMatrix, float4(force, 0.0f)).xyz * {7};\n"
|
||||
" {1} += force * (DeltaTime / {2});\n"
|
||||
" {1} += force * (DeltaTime / max({2}, PARTICLE_THRESHOLD));\n"
|
||||
" }}\n"
|
||||
), position.Value, velocity.Value, mass.Value, fieldPosition.Value, fieldRotation.Value, fieldScale.Value, roughness.Value, intensity.Value, octavesCount.Value);
|
||||
break;
|
||||
@@ -486,21 +486,21 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node)
|
||||
" float3 u = RAND3;\n"
|
||||
" float sinTheta, cosTheta;\n"
|
||||
" sincos(u.x * PI * 2.0f, sinTheta, cosTheta);\n"
|
||||
" float r = saturate((float){4} / {3});\n"
|
||||
" float2 s1_1 = r * float2( cosTheta, sinTheta) + float2(1, 0);\n"
|
||||
" float2 s1_2 = r * float2(-cosTheta, sinTheta) + float2(1, 0);\n"
|
||||
" float w = s1_1.x / (s1_1.x + s1_2.x);\n"
|
||||
" float r = saturate((float){4} / max({3}, PARTICLE_THRESHOLD));\n"
|
||||
" float2 s11 = r * float2( cosTheta, sinTheta) + float2(1, 0);\n"
|
||||
" float2 s12 = r * float2(-cosTheta, sinTheta) + float2(1, 0);\n"
|
||||
" float w = s11.x / (s11.x + s12.x);\n"
|
||||
" float3 t;\n"
|
||||
" float phi;\n"
|
||||
" if (u.y < w)\n"
|
||||
" {{\n"
|
||||
" phi = radians({5}) * u.y / w;\n"
|
||||
" t = float3(s1_1.x, 0, s1_1.y);\n"
|
||||
" t = float3(s11.x, 0, s11.y);\n"
|
||||
" }}\n"
|
||||
" else\n"
|
||||
" {{\n"
|
||||
" phi = radians({5}) * (u.y - w) / (1.0f - w);\n"
|
||||
" t = float3(s1_2.x, 0, s1_2.y);\n"
|
||||
" t = float3(s12.x, 0, s12.y);\n"
|
||||
" }}\n"
|
||||
" float s, c;\n"
|
||||
" sincos(phi, c, s);\n"
|
||||
@@ -693,7 +693,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node)
|
||||
" if ({4} * sqrLength <= {4} * totalRadius * totalRadius)\n"
|
||||
" {{\n"
|
||||
" float dist = sqrt(sqrLength);\n"
|
||||
" float3 n = {4} * dir / dist;\n"
|
||||
" float3 n = {4} * dir / max(dist, PARTICLE_THRESHOLD);\n"
|
||||
" {0} -= n * (dist - totalRadius) * {4};\n"
|
||||
COLLISION_LOGIC()
|
||||
" }}\n"
|
||||
@@ -787,7 +787,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node)
|
||||
" collision = abs(dir.y) > halfHeight || sqrLength > cylinderRadiusT * cylinderRadiusT;\n"
|
||||
" if (collision)\n"
|
||||
" {{\n"
|
||||
" float dist = sqrt(sqrLength);\n"
|
||||
" float dist = max(sqrt(sqrLength), PARTICLE_THRESHOLD);\n"
|
||||
" float distToCap = {4} * (halfHeight - abs(dir.y));\n"
|
||||
" float distToSide = {4} * (cylinderRadiusT - dist);\n"
|
||||
" float3 n = float3(dir.x / dist, sign(dir.y), dir.z / dist) * {4};\n"
|
||||
|
||||
@@ -113,6 +113,131 @@ ParticleEmitterGPUGenerator::Value ParticleEmitterGPUGenerator::AccessParticleAt
|
||||
return value.Variable;
|
||||
}
|
||||
|
||||
void ParticleEmitterGPUGenerator::ProcessGroupParameters(Box* box, Node* node, Value& value)
|
||||
{
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Get
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Get parameter
|
||||
const auto param = findParam((Guid)node->Values[0]);
|
||||
if (param)
|
||||
{
|
||||
switch (param->Type)
|
||||
{
|
||||
case MaterialParameterType::Bool:
|
||||
value = Value(VariantType::Bool, param->ShaderName);
|
||||
break;
|
||||
case MaterialParameterType::Integer:
|
||||
case MaterialParameterType::SceneTexture:
|
||||
value = Value(VariantType::Int, param->ShaderName);
|
||||
break;
|
||||
case MaterialParameterType::Float:
|
||||
value = Value(VariantType::Float, param->ShaderName);
|
||||
break;
|
||||
case MaterialParameterType::Vector2:
|
||||
case MaterialParameterType::Vector3:
|
||||
case MaterialParameterType::Vector4:
|
||||
case MaterialParameterType::Color:
|
||||
{
|
||||
// Set result values based on box ID
|
||||
const Value sample(box->Type.Type, param->ShaderName);
|
||||
switch (box->ID)
|
||||
{
|
||||
case 0:
|
||||
value = sample;
|
||||
break;
|
||||
case 1:
|
||||
value.Value = sample.Value + _subs[0];
|
||||
break;
|
||||
case 2:
|
||||
value.Value = sample.Value + _subs[1];
|
||||
break;
|
||||
case 3:
|
||||
value.Value = sample.Value + _subs[2];
|
||||
break;
|
||||
case 4:
|
||||
value.Value = sample.Value + _subs[3];
|
||||
break;
|
||||
default: CRASH;
|
||||
break;
|
||||
}
|
||||
value.Type = box->Type.Type;
|
||||
break;
|
||||
}
|
||||
|
||||
case MaterialParameterType::Matrix:
|
||||
{
|
||||
value = Value(box->Type.Type, String::Format(TEXT("{0}[{1}]"), param->ShaderName, box->ID));
|
||||
break;
|
||||
}
|
||||
case MaterialParameterType::ChannelMask:
|
||||
{
|
||||
const auto input = tryGetValue(node->GetBox(0), Value::Zero);
|
||||
value = writeLocal(VariantType::Float, String::Format(TEXT("dot({0}, {1})"), input.Value, param->ShaderName), node);
|
||||
break;
|
||||
}
|
||||
case MaterialParameterType::CubeTexture:
|
||||
case MaterialParameterType::Texture:
|
||||
case MaterialParameterType::GPUTextureArray:
|
||||
case MaterialParameterType::GPUTextureCube:
|
||||
case MaterialParameterType::GPUTextureVolume:
|
||||
case MaterialParameterType::GPUTexture:
|
||||
value = Value(VariantType::Object, param->ShaderName);
|
||||
break;
|
||||
default:
|
||||
CRASH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnError(node, box, String::Format(TEXT("Missing graph parameter {0}."), node->Values[0]));
|
||||
value = Value::Zero;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEmitterGPUGenerator::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
{
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Linearize Depth
|
||||
case 7:
|
||||
{
|
||||
// Get input
|
||||
const Value depth = tryGetValue(node->GetBox(0), Value::Zero).AsFloat();
|
||||
|
||||
// Linearize raw device depth
|
||||
linearizeSceneDepth(node, depth, value);
|
||||
break;
|
||||
}
|
||||
// Time
|
||||
case 8:
|
||||
value = box->ID == 0 ? Value(VariantType::Float, TEXT("Time")) : Value(VariantType::Float, TEXT("DeltaTime"));
|
||||
break;
|
||||
// Transform Position To Screen UV
|
||||
case 9:
|
||||
{
|
||||
const Value position = tryGetValue(node->GetBox(0), Value::Zero).AsVector3();
|
||||
const Value projPos = writeLocal(VariantType::Vector4, String::Format(TEXT("mul(float4({0}, 1.0f), ViewProjectionMatrix)"), position.Value), node);
|
||||
_writer.Write(TEXT("\t{0}.xy /= {0}.w;\n"), projPos.Value);
|
||||
_writer.Write(TEXT("\t{0}.xy = {0}.xy * 0.5f + 0.5f;\n"), projPos.Value);
|
||||
value = Value(VariantType::Vector2, projPos.Value + TEXT(".xy"));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ShaderGenerator::ProcessGroupTools(box, node, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Value& value)
|
||||
{
|
||||
switch (node->TypeID)
|
||||
|
||||
@@ -256,10 +256,8 @@ void ParticleEmitterGPUGenerator::ProcessGroupTextures(Box* box, Node* node, Val
|
||||
}
|
||||
// Scene Depth
|
||||
case 8:
|
||||
{
|
||||
sampleSceneDepth(node, value, box);
|
||||
break;
|
||||
}
|
||||
// Texture
|
||||
case 11:
|
||||
{
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_PARTICLE_GPU_GRAPH
|
||||
|
||||
#include "ParticleEmitterGraph.GPU.h"
|
||||
|
||||
void ParticleEmitterGPUGenerator::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
{
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Linearize Depth
|
||||
case 7:
|
||||
{
|
||||
// Get input
|
||||
const Value depth = tryGetValue(node->GetBox(0), Value::Zero).AsFloat();
|
||||
|
||||
// Linearize raw device depth
|
||||
linearizeSceneDepth(node, depth, value);
|
||||
break;
|
||||
}
|
||||
// Time
|
||||
case 8:
|
||||
{
|
||||
value = box->ID == 0 ? Value(VariantType::Float, TEXT("Time")) : Value(VariantType::Float, TEXT("DeltaTime"));
|
||||
break;
|
||||
}
|
||||
// Transform Position To Screen UV
|
||||
case 9:
|
||||
{
|
||||
const Value position = tryGetValue(node->GetBox(0), Value::Zero).AsVector3();
|
||||
const Value projPos = writeLocal(VariantType::Vector4, String::Format(TEXT("mul(float4({0}, 1.0f), ViewProjectionMatrix)"), position.Value), node);
|
||||
_writer.Write(TEXT("\t{0}.xy /= {0}.w;\n"), projPos.Value);
|
||||
_writer.Write(TEXT("\t{0}.xy = {0}.xy * 0.5f + 0.5f;\n"), projPos.Value);
|
||||
value = Value(VariantType::Vector2, projPos.Value + TEXT(".xy"));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ShaderGenerator::ProcessGroupTools(box, node, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -343,6 +343,11 @@ void CharacterController::OnTransformChanged()
|
||||
UpdateScale();
|
||||
UpdateBounds();
|
||||
}
|
||||
else if (!_controller)
|
||||
{
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
|
||||
@@ -284,6 +284,7 @@ protected:
|
||||
|
||||
Vector2 _trackingMouseOffset;
|
||||
bool _isUsingMouseOffset;
|
||||
Rectangle _mouseOffsetScreenSize;
|
||||
bool _isTrackingMouse;
|
||||
|
||||
explicit WindowBase(const CreateWindowSettings& settings);
|
||||
|
||||
@@ -632,8 +632,19 @@ void WindowsPlatform::Exit()
|
||||
|
||||
void WindowsPlatform::Log(const StringView& msg)
|
||||
{
|
||||
OutputDebugStringW(msg.Get());
|
||||
OutputDebugStringW(TEXT(PLATFORM_LINE_TERMINATOR));
|
||||
Char buffer[512];
|
||||
Char* str;
|
||||
if (msg.Length() + 3 < ARRAY_COUNT(buffer))
|
||||
str = buffer;
|
||||
else
|
||||
str = (Char*)Allocate((msg.Length() + 3) * sizeof(Char), 16);
|
||||
MemoryCopy(str, msg.Get(), msg.Length() * sizeof(Char));
|
||||
str[msg.Length() + 0] = '\r';
|
||||
str[msg.Length() + 1] = '\n';
|
||||
str[msg.Length() + 2] = 0;
|
||||
OutputDebugStringW(str);
|
||||
if (str != buffer)
|
||||
Free(str);
|
||||
}
|
||||
|
||||
bool WindowsPlatform::IsDebuggerPresent()
|
||||
|
||||
@@ -479,6 +479,10 @@ void WindowsWindow::StartTrackingMouse(bool useMouseScreenOffset)
|
||||
_trackingMouseOffset = Vector2::Zero;
|
||||
_isUsingMouseOffset = useMouseScreenOffset;
|
||||
|
||||
int32 x = 0 , y = 0, width = 0, height = 0;
|
||||
GetScreenInfo(x, y, width, height);
|
||||
_mouseOffsetScreenSize = Rectangle(x, y, width, height);
|
||||
|
||||
SetCapture(_handle);
|
||||
}
|
||||
}
|
||||
@@ -712,18 +716,20 @@ LRESULT WindowsWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
if (_isTrackingMouse && _isUsingMouseOffset)
|
||||
{
|
||||
// Check if move mouse to another edge of the desktop
|
||||
Vector2 destopSize = Platform::GetVirtualDesktopSize();
|
||||
Vector2 desktopLocation = _mouseOffsetScreenSize.Location;
|
||||
Vector2 destopSize = _mouseOffsetScreenSize.GetBottomRight();
|
||||
|
||||
const Vector2 mousePos(static_cast<float>(WINDOWS_GET_X_LPARAM(lParam)), static_cast<float>(WINDOWS_GET_Y_LPARAM(lParam)));
|
||||
Vector2 mousePosition = ClientToScreen(mousePos);
|
||||
Vector2 newMousePosition = mousePosition;
|
||||
if (mousePosition.X <= 1)
|
||||
if (mousePosition.X <= desktopLocation.X + 2)
|
||||
newMousePosition.X = destopSize.X - 2;
|
||||
else if (mousePosition.X >= destopSize.X - 1)
|
||||
newMousePosition.X = 2;
|
||||
if (mousePosition.Y <= 1)
|
||||
newMousePosition.X = desktopLocation.X + 2;
|
||||
if (mousePosition.Y <= desktopLocation.Y + 2)
|
||||
newMousePosition.Y = destopSize.Y - 2;
|
||||
else if (mousePosition.Y >= destopSize.Y - 1)
|
||||
newMousePosition.Y = 2;
|
||||
newMousePosition.Y = desktopLocation.Y + 2;
|
||||
if (!Vector2::NearEqual(mousePosition, newMousePosition))
|
||||
{
|
||||
_trackingMouseOffset -= newMousePosition - mousePosition;
|
||||
|
||||
@@ -182,41 +182,44 @@ struct ClipMask
|
||||
|
||||
Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping;
|
||||
|
||||
// Private Stuff
|
||||
GPUContext* Context = nullptr;
|
||||
GPUTextureView* Output = nullptr;
|
||||
GPUTextureView* DepthBuffer = nullptr;
|
||||
Viewport View;
|
||||
Matrix ViewProjection;
|
||||
namespace
|
||||
{
|
||||
// Private Stuff
|
||||
GPUContext* Context = nullptr;
|
||||
GPUTextureView* Output = nullptr;
|
||||
GPUTextureView* DepthBuffer = nullptr;
|
||||
Viewport View;
|
||||
Matrix ViewProjection;
|
||||
|
||||
// Drawing
|
||||
Array<Render2DDrawCall> DrawCalls;
|
||||
Array<FontLineCache> Lines;
|
||||
Array<Vector2> Lines2;
|
||||
bool IsScissorsRectEmpty;
|
||||
bool IsScissorsRectEnabled;
|
||||
// Drawing
|
||||
Array<Render2DDrawCall> DrawCalls;
|
||||
Array<FontLineCache> Lines;
|
||||
Array<Vector2> Lines2;
|
||||
bool IsScissorsRectEmpty;
|
||||
bool IsScissorsRectEnabled;
|
||||
|
||||
// Transform
|
||||
// Note: we use Matrix3x3 instead of Matrix because we use only 2D transformations on CPU side
|
||||
// Matrix layout:
|
||||
// [ m1, m2, 0 ]
|
||||
// [ m3, m4, 0 ]
|
||||
// [ t1, t2, 1 ]
|
||||
// where 'm' is 2D transformation (scale, shear and rotate), 't' is translation
|
||||
Array<Matrix3x3, InlinedAllocation<64>> TransformLayersStack;
|
||||
Matrix3x3 TransformCached;
|
||||
// Transform
|
||||
// Note: we use Matrix3x3 instead of Matrix because we use only 2D transformations on CPU side
|
||||
// Matrix layout:
|
||||
// [ m1, m2, 0 ]
|
||||
// [ m3, m4, 0 ]
|
||||
// [ t1, t2, 1 ]
|
||||
// where 'm' is 2D transformation (scale, shear and rotate), 't' is translation
|
||||
Array<Matrix3x3, InlinedAllocation<64>> TransformLayersStack;
|
||||
Matrix3x3 TransformCached;
|
||||
|
||||
Array<ClipMask, InlinedAllocation<64>> ClipLayersStack;
|
||||
Array<ClipMask, InlinedAllocation<64>> ClipLayersStack;
|
||||
|
||||
// Shader
|
||||
AssetReference<Shader> GUIShader;
|
||||
CachedPSO PsoDepth;
|
||||
CachedPSO PsoNoDepth;
|
||||
CachedPSO* CurrentPso = nullptr;
|
||||
DynamicVertexBuffer VB(RENDER2D_INITIAL_VB_CAPACITY, (uint32)sizeof(Render2DVertex), TEXT("Render2D.VB"));
|
||||
DynamicIndexBuffer IB(RENDER2D_INITIAL_IB_CAPACITY, sizeof(uint32), TEXT("Render2D.IB"));
|
||||
uint32 VBIndex = 0;
|
||||
uint32 IBIndex = 0;
|
||||
// Shader
|
||||
AssetReference<Shader> GUIShader;
|
||||
CachedPSO PsoDepth;
|
||||
CachedPSO PsoNoDepth;
|
||||
CachedPSO* CurrentPso = nullptr;
|
||||
DynamicVertexBuffer VB(RENDER2D_INITIAL_VB_CAPACITY, (uint32)sizeof(Render2DVertex), TEXT("Render2D.VB"));
|
||||
DynamicIndexBuffer IB(RENDER2D_INITIAL_IB_CAPACITY, sizeof(uint32), TEXT("Render2D.IB"));
|
||||
uint32 VBIndex = 0;
|
||||
uint32 IBIndex = 0;
|
||||
}
|
||||
|
||||
#define RENDER2D_WRITE_IB_QUAD(indices) \
|
||||
indices[0] = VBIndex + 0; \
|
||||
@@ -957,8 +960,8 @@ void DrawBatch(int32 startIndex, int32 count)
|
||||
data.Bounds.Y = bounds.Y;
|
||||
data.Bounds.Z = bounds.Z - bounds.X;
|
||||
data.Bounds.W = bounds.W - bounds.Y;
|
||||
data.InvBufferSize.X = 1.0f / renderTargetWidth;
|
||||
data.InvBufferSize.Y = 1.0f / renderTargetHeight;
|
||||
data.InvBufferSize.X = 1.0f / (float)renderTargetWidth;
|
||||
data.InvBufferSize.Y = 1.0f / (float)renderTargetHeight;
|
||||
data.SampleCount = ComputeBlurWeights(kernelSize, blurStrength, data.WeightAndOffsets);
|
||||
const auto cb = GUIShader->GetShader()->GetCB(1);
|
||||
Context->UpdateCB(cb, &data);
|
||||
@@ -967,7 +970,7 @@ void DrawBatch(int32 startIndex, int32 count)
|
||||
// Downscale (or not) and extract the background image for the blurring
|
||||
Context->ResetRenderTarget();
|
||||
Context->SetRenderTarget(blurA->View());
|
||||
Context->SetViewport((float)renderTargetWidth, (float)renderTargetHeight);
|
||||
Context->SetViewportAndScissors((float)renderTargetWidth, (float)renderTargetHeight);
|
||||
Context->BindSR(0, Output);
|
||||
Context->SetState(CurrentPso->PS_Downscale);
|
||||
Context->DrawFullscreenTriangle();
|
||||
@@ -1003,11 +1006,8 @@ void DrawBatch(int32 startIndex, int32 count)
|
||||
break;
|
||||
}
|
||||
case DrawCallType::ClipScissors:
|
||||
{
|
||||
Rectangle* scissorsRect = (Rectangle*)&d.AsClipScissors.X;
|
||||
Context->SetScissor(*scissorsRect);
|
||||
Context->SetScissor(*(Rectangle*)&d.AsClipScissors.X);
|
||||
return;
|
||||
}
|
||||
case DrawCallType::LineAA:
|
||||
Context->SetState(CurrentPso->PS_LineAA);
|
||||
break;
|
||||
|
||||
@@ -56,7 +56,45 @@ namespace
|
||||
MDomain* _monoRootDomain = nullptr;
|
||||
MDomain* _monoScriptsDomain = nullptr;
|
||||
CriticalSection _objectsLocker;
|
||||
#define USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING 0
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
struct ScriptingObjectData
|
||||
{
|
||||
ScriptingObject* Ptr;
|
||||
StringAnsi TypeName;
|
||||
|
||||
ScriptingObjectData()
|
||||
{
|
||||
Ptr = nullptr;
|
||||
}
|
||||
|
||||
ScriptingObjectData(ScriptingObject* ptr)
|
||||
{
|
||||
Ptr = ptr;
|
||||
if (ptr && ptr->GetTypeHandle() && ptr->GetTypeHandle().TypeIndex < ptr->GetTypeHandle().Module->Types.Count())
|
||||
TypeName = ptr->GetType().Fullname;
|
||||
}
|
||||
|
||||
ScriptingObject* operator->() const
|
||||
{
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
explicit operator ScriptingObject*()
|
||||
{
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
operator ScriptingObject*() const
|
||||
{
|
||||
return Ptr;
|
||||
}
|
||||
};
|
||||
|
||||
Dictionary<Guid, ScriptingObjectData> _objectsDictionary(1024 * 16);
|
||||
#else
|
||||
Dictionary<Guid, ScriptingObject*> _objectsDictionary(1024 * 16);
|
||||
#endif
|
||||
bool _isEngineAssemblyLoaded = false;
|
||||
bool _hasGameModulesLoaded = false;
|
||||
MMethod* _method_Update = nullptr;
|
||||
@@ -456,6 +494,9 @@ void Scripting::Release()
|
||||
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto obj = i->Value;
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName));
|
||||
#endif
|
||||
obj->OnScriptingDispose();
|
||||
}
|
||||
}
|
||||
@@ -498,7 +539,9 @@ void Scripting::Release()
|
||||
}
|
||||
}
|
||||
|
||||
#if !USE_SINGLE_DOMAIN
|
||||
MCore::Instance()->UnloadDomain("Scripts Domain");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
@@ -546,6 +589,24 @@ void Scripting::Reload(bool canTriggerSceneReload)
|
||||
MCore::GC::Collect();
|
||||
MCore::GC::WaitForPendingFinalizers();
|
||||
|
||||
// Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload)
|
||||
_objectsLocker.Lock();
|
||||
{
|
||||
const auto flaxModule = GetBinaryModuleFlaxEngine();
|
||||
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto obj = i->Value;
|
||||
if (obj->GetTypeHandle().Module == flaxModule)
|
||||
continue;
|
||||
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName));
|
||||
#endif
|
||||
obj->OnScriptingDispose();
|
||||
}
|
||||
}
|
||||
_objectsLocker.Unlock();
|
||||
|
||||
// Unload all game modules
|
||||
LOG(Info, "Unloading game binary modules");
|
||||
auto modules = BinaryModule::GetModules();
|
||||
@@ -683,10 +744,18 @@ ScriptingObject* Scripting::FindObject(Guid id, MClass* type)
|
||||
}
|
||||
|
||||
// Try to find it
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
ScriptingObjectData data;
|
||||
_objectsLocker.Lock();
|
||||
_objectsDictionary.TryGet(id, data);
|
||||
_objectsLocker.Unlock();
|
||||
auto result = data.Ptr;
|
||||
#else
|
||||
ScriptingObject* result = nullptr;
|
||||
_objectsLocker.Lock();
|
||||
_objectsDictionary.TryGet(id, result);
|
||||
_objectsLocker.Unlock();
|
||||
#endif
|
||||
if (result)
|
||||
{
|
||||
// Check type
|
||||
@@ -718,11 +787,20 @@ ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type)
|
||||
}
|
||||
|
||||
// Try to find it
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
ScriptingObjectData data;
|
||||
_objectsLocker.Lock();
|
||||
_objectsDictionary.TryGet(id, data);
|
||||
_objectsLocker.Unlock();
|
||||
auto result = data.Ptr;
|
||||
#else
|
||||
ScriptingObject* result = nullptr;
|
||||
_objectsLocker.Lock();
|
||||
_objectsDictionary.TryGet(id, result);
|
||||
_objectsLocker.Unlock();
|
||||
#endif
|
||||
|
||||
// Check type
|
||||
if (result && !result->Is(type))
|
||||
{
|
||||
result = nullptr;
|
||||
@@ -753,28 +831,23 @@ ScriptingObject* Scripting::FindObject(const MonoObject* managedInstance)
|
||||
|
||||
void Scripting::OnManagedInstanceDeleted(ScriptingObject* obj)
|
||||
{
|
||||
PROFILE_CPU_NAMED("OnManagedInstanceDeleted");
|
||||
ASSERT(obj);
|
||||
|
||||
PROFILE_CPU_NAMED("OnManagedInstanceDeleted");
|
||||
|
||||
// This is sometimes crashing, probably rawPtr field is not cleared in some cases
|
||||
// TODO: use faster callback without crashing
|
||||
//obj->OnManagedInstanceDeleted();
|
||||
|
||||
// Validate if object still exists
|
||||
bool isValid;
|
||||
{
|
||||
ScopeLock lock(_objectsLocker);
|
||||
isValid = _objectsDictionary.ContainsValue(obj);
|
||||
}
|
||||
if (isValid)
|
||||
_objectsLocker.Lock();
|
||||
if (_objectsDictionary.ContainsValue(obj))
|
||||
{
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
LOG(Info, "[OnManagedInstanceDeleted] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName));
|
||||
#endif
|
||||
obj->OnManagedInstanceDeleted();
|
||||
}
|
||||
else
|
||||
{
|
||||
//LOG(Warning, "Object finalization called for already removed object (address={0:x})", (uint64)obj);
|
||||
}
|
||||
_objectsLocker.Unlock();
|
||||
}
|
||||
|
||||
bool Scripting::HasGameModulesLoaded()
|
||||
@@ -805,18 +878,25 @@ void Scripting::RegisterObject(ScriptingObject* obj)
|
||||
|
||||
//ASSERT(!_objectsDictionary.ContainsValue(obj));
|
||||
#if ENABLE_ASSERTION
|
||||
ScriptingObject* other = nullptr;
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
ScriptingObjectData other;
|
||||
if (_objectsDictionary.TryGet(obj->GetID(), other))
|
||||
#else
|
||||
ScriptingObject* other;
|
||||
if (_objectsDictionary.TryGet(obj->GetID(), other))
|
||||
#endif
|
||||
{
|
||||
// Something went wrong...
|
||||
LOG(Error, "Objects registry already contains object with ID={0}! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName()));
|
||||
|
||||
LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName()));
|
||||
_objectsDictionary.Remove(obj->GetID());
|
||||
}
|
||||
#else
|
||||
ASSERT(!_objectsDictionary.ContainsKey(obj->_id));
|
||||
#endif
|
||||
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
LOG(Info, "[RegisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName));
|
||||
#endif
|
||||
_objectsDictionary.Add(obj->GetID(), obj);
|
||||
}
|
||||
|
||||
@@ -826,6 +906,9 @@ void Scripting::UnregisterObject(ScriptingObject* obj)
|
||||
|
||||
//ASSERT(!obj->_id.IsValid() || _objectsDictionary.ContainsValue(obj));
|
||||
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
LOG(Info, "[UnregisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName));
|
||||
#endif
|
||||
_objectsDictionary.Remove(obj->GetID());
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "ManagedCLR/MClass.h"
|
||||
#include "ManagedCLR/MUtils.h"
|
||||
#include "ManagedCLR/MField.h"
|
||||
#include "ManagedCLR/MUtils.h"
|
||||
#if PLATFORM_LINUX
|
||||
#include "ManagedCLR/MCore.h"
|
||||
#endif
|
||||
@@ -515,11 +514,6 @@ public:
|
||||
obj->RegisterObject();
|
||||
}
|
||||
|
||||
static void ManagedInstanceDeleted(ScriptingObject* obj)
|
||||
{
|
||||
Scripting::OnManagedInstanceDeleted(obj);
|
||||
}
|
||||
|
||||
static void Destroy(ManagedScriptingObject* obj, float timeLeft)
|
||||
{
|
||||
// Use scaled game time for removing actors/scripts by the user (maybe expose it to the api?)
|
||||
@@ -558,7 +552,7 @@ public:
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create1", &Create1);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create2", &Create2);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceCreated", &ManagedInstanceCreated);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &ManagedInstanceDeleted);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &Scripting::OnManagedInstanceDeleted);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Destroy", &Destroy);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetTypeName", &GetTypeName);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_FindObject", &FindObject);
|
||||
|
||||
@@ -36,6 +36,7 @@ Terrain::~Terrain()
|
||||
|
||||
void Terrain::UpdateBounds()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
for (int32 i = 0; i < _patches.Count(); i++)
|
||||
{
|
||||
@@ -48,6 +49,7 @@ void Terrain::UpdateBounds()
|
||||
|
||||
void Terrain::CacheNeighbors()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
|
||||
{
|
||||
const auto patch = _patches[pathIndex];
|
||||
|
||||
@@ -188,7 +188,7 @@ bool TerrainChunk::Intersects(const Ray& ray, float& distance)
|
||||
void TerrainChunk::UpdateBounds()
|
||||
{
|
||||
const Vector3 boundsExtent = _patch->_terrain->_boundsExtent;
|
||||
const float size = _patch->_terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX;
|
||||
const float size = (float)_patch->_terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX;
|
||||
Transform terrainTransform = _patch->_terrain->_transform;
|
||||
|
||||
Transform localTransform;
|
||||
|
||||
@@ -951,7 +951,7 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap,
|
||||
chunk._yHeight = chunkHeights[chunkIndex];
|
||||
chunk.UpdateTransform();
|
||||
}
|
||||
UpdateBounds();
|
||||
_terrain->UpdateBounds();
|
||||
UpdateCollision();
|
||||
|
||||
#if TERRAIN_UPDATING
|
||||
@@ -1431,7 +1431,7 @@ bool TerrainPatch::ModifyHeightMap(const float* samples, const Int2& modifiedOff
|
||||
chunk._yHeight = chunkHeights[chunkIndex];
|
||||
chunk.UpdateTransform();
|
||||
}
|
||||
UpdateBounds();
|
||||
_terrain->UpdateBounds();
|
||||
return UpdateHeightData(info, modifiedOffset, modifiedSize, wasHeightRangeChanged);
|
||||
}
|
||||
|
||||
@@ -2108,9 +2108,8 @@ void TerrainPatch::UpdatePostManualDeserialization()
|
||||
{
|
||||
auto& chunk = Chunks[chunkIndex];
|
||||
chunk.UpdateTransform();
|
||||
chunk.UpdateBounds();
|
||||
}
|
||||
UpdateBounds();
|
||||
_terrain->UpdateBounds();
|
||||
|
||||
ScopeLock lock(_collisionLocker);
|
||||
|
||||
|
||||
@@ -342,24 +342,28 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
// Blend Normals
|
||||
case 26:
|
||||
{
|
||||
const auto baseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3();
|
||||
const auto additionalNormal = tryGetValue(node->GetBox(1), Value::Zero).AsVector3();
|
||||
const String text = String::Format(TEXT("float3((float2({0}.xy) + float2({1}.xy) * 2.0), sqrt(saturate(1.0 - dot((float2({0}.xy) + float2({1}.xy) * 2.0).xy, (float2({0}.xy) + float2({1}.xy) * 2.0).xy))))"), baseNormal.Value, additionalNormal.Value);
|
||||
value = writeLocal(ValueType::Vector3, text, node);
|
||||
const auto baseNormal = tryGetValue(node->GetBox(0), getNormalZero).AsVector3();
|
||||
const auto additionalNormal = tryGetValue(node->GetBox(1), getNormalZero).AsVector3();
|
||||
|
||||
const String text1 = String::Format(TEXT("(float2({0}.xy) + float2({1}.xy) * 2.0)"), baseNormal.Value, additionalNormal.Value);
|
||||
const auto appendXY = writeLocal(ValueType::Vector2, text1, node);
|
||||
|
||||
const String text2 = String::Format(TEXT("float3({0}, sqrt(saturate(1.0 - dot({0}.xy, {0}.xy))))"), appendXY.Value);
|
||||
value = writeLocal(ValueType::Vector3, text2, node);
|
||||
break;
|
||||
}
|
||||
// Rotator
|
||||
case 27:
|
||||
{
|
||||
auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2();
|
||||
auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2();
|
||||
auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat();
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2();
|
||||
const auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2();
|
||||
const auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat();
|
||||
|
||||
const auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, uv.Value), node);
|
||||
const auto raCosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node);
|
||||
auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, uv.Value), node);
|
||||
auto raCosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node);
|
||||
|
||||
const auto dotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), raCosSin.Value), node);
|
||||
const auto dotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), raCosSin.Value), node);
|
||||
auto dotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), raCosSin.Value), node);
|
||||
auto dotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), raCosSin.Value), node);
|
||||
|
||||
value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, dotB1.Value, dotB2.Value, center.Value), node);
|
||||
break;
|
||||
@@ -367,11 +371,11 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
// Sphere Mask
|
||||
case 28:
|
||||
{
|
||||
Value a = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
Value b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type);
|
||||
Value radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat();
|
||||
Value hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat();
|
||||
Value invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool();
|
||||
const auto a = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
const auto b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type);
|
||||
const auto radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat();
|
||||
const auto hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat();
|
||||
const auto invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool();
|
||||
|
||||
// Get distance and apply radius
|
||||
auto x1 = writeLocal(ValueType::Float, String::Format(TEXT("distance({0},{1}) * (1 / {2})"), a.Value, b.Value, radius.Value), node);
|
||||
@@ -385,9 +389,9 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
// Tiling & Offset
|
||||
case 29:
|
||||
{
|
||||
auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2();
|
||||
auto tiling = tryGetValue(node->GetBox(1), node->Values[0]).AsVector2();
|
||||
auto offset = tryGetValue(node->GetBox(2), node->Values[1]).AsVector2();
|
||||
const auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2();
|
||||
const auto tiling = tryGetValue(node->GetBox(1), node->Values[0]).AsVector2();
|
||||
const auto offset = tryGetValue(node->GetBox(2), node->Values[1]).AsVector2();
|
||||
|
||||
value = writeLocal(ValueType::Vector2, String::Format(TEXT("{0} * {1} + {2}"), uv.Value, tiling.Value, offset.Value), node);
|
||||
break;
|
||||
@@ -395,7 +399,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
// DDX
|
||||
case 30:
|
||||
{
|
||||
auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
|
||||
value = writeLocal(inValue.Type, String::Format(TEXT("ddx({0})"), inValue.Value), node);
|
||||
break;
|
||||
@@ -403,10 +407,56 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
// DDY
|
||||
case 31:
|
||||
{
|
||||
auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
|
||||
value = writeLocal(inValue.Type, String::Format(TEXT("ddy({0})"), inValue.Value), node);
|
||||
break;
|
||||
}
|
||||
// Sign
|
||||
case 32:
|
||||
{
|
||||
const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
|
||||
value = writeLocal(ValueType::Float, String::Format(TEXT("sign({0})"), inValue.Value), node);
|
||||
break;
|
||||
}
|
||||
// Any
|
||||
case 33:
|
||||
{
|
||||
const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
|
||||
value = writeLocal(ValueType::Bool, String::Format(TEXT("any({0})"), inValue.Value), node);
|
||||
break;
|
||||
}
|
||||
// All
|
||||
case 34:
|
||||
{
|
||||
const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero);
|
||||
|
||||
value = writeLocal(ValueType::Bool, String::Format(TEXT("all({0})"), inValue.Value), node);
|
||||
break;
|
||||
}
|
||||
// Blackbody
|
||||
case 35:
|
||||
{
|
||||
// Reference: Mitchell Charity, http://www.vendian.org/mncharity/dir3/blackbody/
|
||||
|
||||
const auto temperature = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat();
|
||||
|
||||
// Value X
|
||||
auto x = writeLocal(ValueType::Float, String::Format(TEXT("56100000.0f * pow({0}, -1) + 148.0f"), temperature.Value), node);
|
||||
|
||||
// Value Y
|
||||
auto y = writeLocal(ValueType::Float, String::Format(TEXT("{0} > 6500.0f ? 35200000.0f * pow({0}, -1) + 184.0f : 100.04f * log({0}) - 623.6f"), temperature.Value), node);
|
||||
|
||||
// Value Z
|
||||
auto z = writeLocal(ValueType::Float, String::Format(TEXT("194.18f * log({0}) - 1448.6f"), temperature.Value), node);
|
||||
|
||||
// Final color
|
||||
auto color = writeLocal(ValueType::Vector3, String::Format(TEXT("float3({0}, {1}, {2})"), x.Value, y.Value, z.Value), node);
|
||||
color = writeLocal(ValueType::Vector3, String::Format(TEXT("clamp({0}, 0.0f, 255.0f) / 255.0f"), color.Value), node);
|
||||
value = writeLocal(ValueType::Vector3, String::Format(TEXT("{1} < 1000.0f ? {0} * {1}/1000.0f : {0}"), color.Value, temperature.Value), node);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -105,6 +105,7 @@ bool FeatureData::Init()
|
||||
MaterialValue MaterialGenerator::getUVs(VariantType::Vector2, TEXT("input.TexCoord"));
|
||||
MaterialValue MaterialGenerator::getTime(VariantType::Float, TEXT("TimeParam"));
|
||||
MaterialValue MaterialGenerator::getNormal(VariantType::Vector3, TEXT("input.TBN[2]"));
|
||||
MaterialValue MaterialGenerator::getNormalZero(VariantType::Vector3, TEXT("float3(0, 0, 1)"));
|
||||
MaterialValue MaterialGenerator::getVertexColor(VariantType::Vector4, TEXT("GetVertexColor(input)"));
|
||||
|
||||
MaterialGenerator::MaterialGenerator()
|
||||
|
||||
@@ -205,6 +205,7 @@ public:
|
||||
static MaterialValue getUVs;
|
||||
static MaterialValue getTime;
|
||||
static MaterialValue getNormal;
|
||||
static MaterialValue getNormalZero;
|
||||
static MaterialValue getVertexColor;
|
||||
static MaterialGraphBoxesMapping MaterialGraphBoxesMappings[];
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/ContentImporters/CreateMaterial.h"
|
||||
#include "ThirdParty/meshoptimizer/meshoptimizer.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
#include <ThirdParty/meshoptimizer/meshoptimizer.h>
|
||||
|
||||
void RemoveNamespace(String& name)
|
||||
{
|
||||
@@ -486,6 +487,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt
|
||||
if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Textures) == 0 || texture.FilePath.IsEmpty())
|
||||
continue;
|
||||
auto filename = StringUtils::GetFileNameWithoutExtension(texture.FilePath);
|
||||
for (int32 j = filename.Length() - 1; j >= 0; j--)
|
||||
{
|
||||
if (EditorUtilities::IsInvalidPathChar(filename[j]))
|
||||
filename[j] = ' ';
|
||||
}
|
||||
if (importedFileNames.Contains(filename))
|
||||
{
|
||||
int32 counter = 1;
|
||||
@@ -526,6 +532,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt
|
||||
if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Materials) == 0 || !material.UsesProperties())
|
||||
continue;
|
||||
auto filename = material.Name;
|
||||
for (int32 j = filename.Length() - 1; j >= 0; j--)
|
||||
{
|
||||
if (EditorUtilities::IsInvalidPathChar(filename[j]))
|
||||
filename[j] = ' ';
|
||||
}
|
||||
if (importedFileNames.Contains(filename))
|
||||
{
|
||||
int32 counter = 1;
|
||||
|
||||
@@ -942,6 +942,8 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
base.OnLostFocus();
|
||||
|
||||
if (IsReadOnly)
|
||||
return;
|
||||
OnEditEnd();
|
||||
}
|
||||
|
||||
|
||||
@@ -1006,10 +1006,9 @@ namespace FlaxEngine.GUI
|
||||
|
||||
c = c.Parent;
|
||||
if (c == parent)
|
||||
return location;
|
||||
break;
|
||||
}
|
||||
|
||||
throw new ArgumentException();
|
||||
return location;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -42,10 +42,10 @@ namespace FlaxEngine.GUI
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
Control c = _children[i];
|
||||
if (c.Visible)
|
||||
if (c.Visible && Mathf.IsZero(c.AnchorMax.X))
|
||||
{
|
||||
var w = c.Width;
|
||||
c.Bounds = new Rectangle(x + _offset.X, _margin.Top + _offset.Y, h, w);
|
||||
c.Bounds = new Rectangle(x + _offset.X, _margin.Top + _offset.Y, w, h);
|
||||
x = c.Right + _spacing;
|
||||
hasAnyItem = true;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace FlaxEngine.GUI
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
Control c = _children[i];
|
||||
if (c.Visible)
|
||||
if (c.Visible && Mathf.IsZero(c.AnchorMax.Y))
|
||||
{
|
||||
var h = c.Height;
|
||||
c.Bounds = new Rectangle(_margin.Left + _offset.X, y + _offset.Y, w, h);
|
||||
|
||||
@@ -17,13 +17,17 @@ MMethod* UICanvas_OnDisable = nullptr;
|
||||
MMethod* UICanvas_EndPlay = nullptr;
|
||||
|
||||
#define UICANVAS_INVOKE(event) \
|
||||
MonoObject* exception = nullptr; \
|
||||
UICanvas_##event->Invoke(GetManagedInstance(), nullptr, &exception); \
|
||||
if (exception) \
|
||||
{ \
|
||||
MException ex(exception); \
|
||||
ex.Log(LogType::Error, TEXT("UICanvas::" #event)); \
|
||||
}
|
||||
auto instance = GetManagedInstance(); \
|
||||
if (instance) \
|
||||
{ \
|
||||
MonoObject* exception = nullptr; \
|
||||
UICanvas_##event->Invoke(instance, nullptr, &exception); \
|
||||
if (exception) \
|
||||
{ \
|
||||
MException ex(exception); \
|
||||
ex.Log(LogType::Error, TEXT("UICanvas::" #event)); \
|
||||
} \
|
||||
}
|
||||
|
||||
UICanvas::UICanvas(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
@@ -279,16 +280,41 @@ namespace FlaxEngine
|
||||
else if (_renderMode == CanvasRenderMode.CameraSpace)
|
||||
{
|
||||
Matrix tmp1, tmp2;
|
||||
Vector3 viewPos, viewUp, viewForward, pos;
|
||||
Quaternion viewRot;
|
||||
|
||||
// Use default camera is not specified
|
||||
var camera = RenderCamera ?? Camera.MainCamera;
|
||||
|
||||
#if FLAX_EDITOR
|
||||
if (_editorTask)
|
||||
{
|
||||
// Use editor viewport task to override Camera Space placement
|
||||
var view = _editorTask.View;
|
||||
var frustum = view.Frustum;
|
||||
if (!frustum.IsOrthographic)
|
||||
_guiRoot.Size = new Vector2(frustum.GetWidthAtDepth(Distance), frustum.GetHeightAtDepth(Distance));
|
||||
else
|
||||
_guiRoot.Size = _editorTask.Viewport.Size;
|
||||
Matrix.Translation(_guiRoot.Width / -2.0f, _guiRoot.Height / -2.0f, 0, out world);
|
||||
Matrix.RotationYawPitchRoll(Mathf.Pi, Mathf.Pi, 0, out tmp2);
|
||||
Matrix.Multiply(ref world, ref tmp2, out tmp1);
|
||||
viewPos = view.Position;
|
||||
viewRot = view.Direction != Vector3.Up ? Quaternion.LookRotation(view.Direction, Vector3.Up) : Quaternion.LookRotation(view.Direction, Vector3.Right);
|
||||
viewUp = Vector3.Up * viewRot;
|
||||
viewForward = view.Direction;
|
||||
pos = view.Position + view.Direction * Distance;
|
||||
Matrix.Billboard(ref pos, ref viewPos, ref viewUp, ref viewForward, out tmp2);
|
||||
Matrix.Multiply(ref tmp1, ref tmp2, out world);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Adjust GUI size to the viewport size at the given distance form the camera
|
||||
var viewport = camera.Viewport;
|
||||
if (camera.UsePerspective)
|
||||
{
|
||||
Matrix tmp3;
|
||||
camera.GetMatrices(out tmp1, out tmp3, ref viewport);
|
||||
camera.GetMatrices(out tmp1, out var tmp3, ref viewport);
|
||||
Matrix.Multiply(ref tmp1, ref tmp3, out tmp2);
|
||||
var frustum = new BoundingFrustum(tmp2);
|
||||
_guiRoot.Size = new Vector2(frustum.GetWidthAtDepth(Distance), frustum.GetHeightAtDepth(Distance));
|
||||
@@ -304,11 +330,11 @@ namespace FlaxEngine
|
||||
Matrix.Multiply(ref world, ref tmp2, out tmp1);
|
||||
|
||||
// In front of the camera
|
||||
var viewPos = camera.Position;
|
||||
var viewRot = camera.Orientation;
|
||||
var viewUp = Vector3.Up * viewRot;
|
||||
var viewForward = Vector3.Forward * viewRot;
|
||||
var pos = viewPos + viewForward * Distance;
|
||||
viewPos = camera.Position;
|
||||
viewRot = camera.Orientation;
|
||||
viewUp = Vector3.Up * viewRot;
|
||||
viewForward = Vector3.Forward * viewRot;
|
||||
pos = viewPos + viewForward * Distance;
|
||||
Matrix.Billboard(ref pos, ref viewPos, ref viewUp, ref viewForward, out tmp2);
|
||||
|
||||
Matrix.Multiply(ref tmp1, ref tmp2, out world);
|
||||
@@ -334,11 +360,18 @@ namespace FlaxEngine
|
||||
_guiRoot.Offsets = Margin.Zero;
|
||||
if (_renderer)
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
_editorTask?.CustomPostFx.Remove(_renderer);
|
||||
#endif
|
||||
SceneRenderTask.GlobalCustomPostFx.Remove(_renderer);
|
||||
_renderer.Canvas = null;
|
||||
Destroy(_renderer);
|
||||
_renderer = null;
|
||||
}
|
||||
#if FLAX_EDITOR
|
||||
if (_editorRoot != null)
|
||||
_guiRoot.Parent = _editorRoot;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case CanvasRenderMode.CameraSpace:
|
||||
@@ -346,12 +379,31 @@ namespace FlaxEngine
|
||||
{
|
||||
// Render canvas manually
|
||||
_guiRoot.AnchorPreset = AnchorPresets.TopLeft;
|
||||
#if FLAX_EDITOR
|
||||
if (_editorRoot != null && _guiRoot != null)
|
||||
_guiRoot.Parent = null;
|
||||
#endif
|
||||
if (_renderer == null)
|
||||
{
|
||||
_renderer = New<CanvasRenderer>();
|
||||
_renderer.Canvas = this;
|
||||
if (IsActiveInHierarchy && Scene)
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
if (_editorTask != null)
|
||||
{
|
||||
_editorTask.CustomPostFx.Add(_renderer);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
SceneRenderTask.GlobalCustomPostFx.Add(_renderer);
|
||||
}
|
||||
#if FLAX_EDITOR
|
||||
else if (_editorTask != null && IsActiveInHierarchy)
|
||||
{
|
||||
_editorTask.CustomPostFx.Add(_renderer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -490,10 +542,21 @@ namespace FlaxEngine
|
||||
|
||||
internal void OnEnable()
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
_guiRoot.Parent = _editorRoot ?? RootControl.CanvasRoot;
|
||||
#else
|
||||
_guiRoot.Parent = RootControl.CanvasRoot;
|
||||
#endif
|
||||
|
||||
if (_renderer)
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
if (_editorTask != null)
|
||||
{
|
||||
_editorTask.CustomPostFx.Add(_renderer);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
SceneRenderTask.GlobalCustomPostFx.Add(_renderer);
|
||||
}
|
||||
}
|
||||
@@ -518,5 +581,25 @@ namespace FlaxEngine
|
||||
_renderer = null;
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
private SceneRenderTask _editorTask;
|
||||
private ContainerControl _editorRoot;
|
||||
|
||||
internal void EditorOverride(SceneRenderTask task, ContainerControl root)
|
||||
{
|
||||
if (_editorTask != null && _renderer != null)
|
||||
_editorTask.CustomPostFx.Remove(_renderer);
|
||||
if (_editorRoot != null && _guiRoot != null)
|
||||
_guiRoot.Parent = null;
|
||||
|
||||
_editorTask = task;
|
||||
_editorRoot = root;
|
||||
Setup();
|
||||
|
||||
if (RenderMode == CanvasRenderMode.ScreenSpace && _editorRoot != null && _guiRoot != null)
|
||||
_guiRoot.Parent = _editorRoot;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace FlaxEngine
|
||||
partial class UIControl
|
||||
{
|
||||
private Control _control;
|
||||
private static bool _blockEvents; // Used to ignore internal events from C++ UIControl impl when performing state sync with C# UI
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the GUI control used by this actor.
|
||||
@@ -30,10 +31,11 @@ namespace FlaxEngine
|
||||
return;
|
||||
|
||||
// Cleanup previous
|
||||
if (_control != null)
|
||||
var prevControl = _control;
|
||||
if (prevControl != null)
|
||||
{
|
||||
_control.LocationChanged -= OnControlLocationChanged;
|
||||
_control.Dispose();
|
||||
prevControl.LocationChanged -= OnControlLocationChanged;
|
||||
prevControl.Dispose();
|
||||
}
|
||||
|
||||
// Set value
|
||||
@@ -42,16 +44,18 @@ namespace FlaxEngine
|
||||
// Link the new one (events and parent)
|
||||
if (_control != null)
|
||||
{
|
||||
// Setup control
|
||||
_blockEvents = true;
|
||||
var containerControl = _control as ContainerControl;
|
||||
if (containerControl != null)
|
||||
containerControl.UnlockChildrenRecursive();
|
||||
|
||||
_control.Parent = GetParent();
|
||||
_control.IndexInParent = OrderInParent;
|
||||
_control.Location = new Vector2(LocalPosition);
|
||||
// TODO: sync control order in parent with actor order in parent (think about special cases like Panel with scroll bars used as internal controls)
|
||||
_control.LocationChanged += OnControlLocationChanged;
|
||||
|
||||
// Link children UI controls
|
||||
if (containerControl != null && IsActiveInHierarchy)
|
||||
{
|
||||
var children = ChildrenCount;
|
||||
@@ -64,6 +68,13 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh
|
||||
_blockEvents = false;
|
||||
if (prevControl == null && _control.Parent != null)
|
||||
_control.Parent.PerformLayout();
|
||||
else
|
||||
_control.PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +181,9 @@ namespace FlaxEngine
|
||||
|
||||
private void OnControlLocationChanged(Control control)
|
||||
{
|
||||
_blockEvents = true;
|
||||
LocalPosition = new Vector3(control.Location, LocalPosition.Z);
|
||||
_blockEvents = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -285,7 +298,7 @@ namespace FlaxEngine
|
||||
|
||||
internal void ParentChanged()
|
||||
{
|
||||
if (_control != null)
|
||||
if (_control != null && !_blockEvents)
|
||||
{
|
||||
_control.Parent = GetParent();
|
||||
_control.IndexInParent = OrderInParent;
|
||||
@@ -294,13 +307,15 @@ namespace FlaxEngine
|
||||
|
||||
internal void TransformChanged()
|
||||
{
|
||||
if (_control != null)
|
||||
if (_control != null && !_blockEvents)
|
||||
{
|
||||
_control.Location = new Vector2(LocalPosition);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ActiveInTreeChanged()
|
||||
{
|
||||
if (_control != null)
|
||||
if (_control != null && !_blockEvents)
|
||||
{
|
||||
// Link or unlink control (won't modify Enable/Visible state)
|
||||
_control.Parent = GetParent();
|
||||
@@ -310,8 +325,10 @@ namespace FlaxEngine
|
||||
|
||||
internal void OrderInParentChanged()
|
||||
{
|
||||
if (_control != null)
|
||||
if (_control != null && !_blockEvents)
|
||||
{
|
||||
_control.IndexInParent = OrderInParent;
|
||||
}
|
||||
}
|
||||
|
||||
internal void BeginPlay()
|
||||
|
||||
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll
(Stored with Git LFS)
vendored
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb
(Stored with Git LFS)
vendored
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb
(Stored with Git LFS)
vendored
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb
(Stored with Git LFS)
vendored
Binary file not shown.
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib
(Stored with Git LFS)
vendored
BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib
(Stored with Git LFS)
vendored
Binary file not shown.
2
Source/ThirdParty/OpenFBX/ofbx.cpp
vendored
2
Source/ThirdParty/OpenFBX/ofbx.cpp
vendored
@@ -2894,7 +2894,7 @@ static float getFramerateFromTimeMode(FrameRate time_mode, float custom_frame_ra
|
||||
{
|
||||
switch (time_mode)
|
||||
{
|
||||
case FrameRate_DEFAULT: return 1;
|
||||
case FrameRate_DEFAULT: return 14;
|
||||
case FrameRate_120: return 120;
|
||||
case FrameRate_100: return 100;
|
||||
case FrameRate_60: return 60;
|
||||
|
||||
13
Source/ThirdParty/rapidjson/document.h
vendored
13
Source/ThirdParty/rapidjson/document.h
vendored
@@ -1679,7 +1679,18 @@ public:
|
||||
//@{
|
||||
|
||||
const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
|
||||
::String GetText() const { RAPIDJSON_ASSERT(IsString()); return ::String(GetString(), GetStringLength()); }
|
||||
::String GetText() const
|
||||
{
|
||||
::String result;
|
||||
if (IsString())
|
||||
{
|
||||
if (data_.f.flags & kInlineStrFlag)
|
||||
result.Set(data_.ss.str, data_.ss.GetLength());
|
||||
else
|
||||
result.Set(GetStringPointer(), data_.s.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Get the length of string.
|
||||
/*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
|
||||
|
||||
@@ -528,10 +528,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
if (comment.Contains("/// <returns>"))
|
||||
continue;
|
||||
var c = comment.Replace("::", ".");
|
||||
contents.Append(indent);
|
||||
contents.Append(c);
|
||||
contents.AppendLine();
|
||||
contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine();
|
||||
}
|
||||
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, eventInfo, useUnmanaged);
|
||||
@@ -626,11 +623,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
if (comment.Contains("/// <returns>"))
|
||||
continue;
|
||||
|
||||
var c = comment.Replace("::", ".");
|
||||
contents.Append(indent);
|
||||
contents.Append(c);
|
||||
contents.AppendLine();
|
||||
contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine();
|
||||
}
|
||||
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, fieldInfo, useUnmanaged, fieldInfo.DefaultValue);
|
||||
@@ -690,14 +683,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
if (comment.Contains("/// <returns>") || comment.Contains("<param name="))
|
||||
continue;
|
||||
|
||||
var c = comment.Replace("::", ".");
|
||||
contents.Append(indent);
|
||||
if (propertyInfo.Getter != null && propertyInfo.Setter != null)
|
||||
contents.Append(c.Replace("/// Gets ", "/// Gets or sets "));
|
||||
else
|
||||
contents.Append(c);
|
||||
contents.AppendLine();
|
||||
contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine();
|
||||
}
|
||||
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, propertyInfo, useUnmanaged);
|
||||
|
||||
@@ -786,6 +786,35 @@ namespace Flax.Build.Bindings
|
||||
else
|
||||
propertyInfo.Setter = functionInfo;
|
||||
|
||||
if (propertyInfo.Getter != null && propertyInfo.Setter != null)
|
||||
{
|
||||
// Check if getter and setter types are matching (const and ref specifiers are skipped)
|
||||
var getterType = propertyInfo.Getter.ReturnType;
|
||||
var setterType = propertyInfo.Setter.Parameters[0].Type;
|
||||
if (!string.Equals(getterType.Type, setterType.Type) ||
|
||||
getterType.IsPtr != setterType.IsPtr ||
|
||||
getterType.IsArray != setterType.IsArray ||
|
||||
getterType.IsBitField != setterType.IsBitField ||
|
||||
getterType.ArraySize != setterType.ArraySize ||
|
||||
getterType.BitSize != setterType.BitSize ||
|
||||
!TypeInfo.Equals(getterType.GenericArgs, setterType.GenericArgs))
|
||||
{
|
||||
// Skip compatible types
|
||||
if (getterType.Type == "String" && setterType.Type == "StringView")
|
||||
return propertyInfo;
|
||||
if (getterType.Type == "Array" && setterType.Type == "Span" && getterType.GenericArgs?.Count == 1 && setterType.GenericArgs?.Count == 1 && getterType.GenericArgs[0].Equals(setterType.GenericArgs[0]))
|
||||
return propertyInfo;
|
||||
throw new Exception($"Property {propertyName} in class {classInfo.Name} (line {context.Tokenizer.CurrentLine}) has mismatching getter return type ({getterType}) and setter parameter type ({setterType}). Both getter and setter methods must use the same value type used for property.");
|
||||
}
|
||||
|
||||
// Fix documentation comment to reflect both getter and setters available
|
||||
for (var i = 0; i < propertyInfo.Comment.Length; i++)
|
||||
{
|
||||
ref var comment = ref propertyInfo.Comment[i];
|
||||
comment = comment.Replace("/// Gets ", "/// Gets or sets ");
|
||||
}
|
||||
}
|
||||
|
||||
return propertyInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Flax.Build.Bindings
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static bool Equals(List<TypeInfo> a, List<TypeInfo> b)
|
||||
public static bool Equals(List<TypeInfo> a, List<TypeInfo> b)
|
||||
{
|
||||
if (a == null && b == null)
|
||||
return true;
|
||||
|
||||
@@ -258,9 +258,8 @@ namespace Flax.Deps.Dependencies
|
||||
"mono_type_normalize",
|
||||
};
|
||||
|
||||
private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture)
|
||||
private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture, string configuration = "Release")
|
||||
{
|
||||
var configuration = "Release";
|
||||
string buildPlatform;
|
||||
switch (architecture)
|
||||
{
|
||||
@@ -491,12 +490,13 @@ namespace Flax.Deps.Dependencies
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
{
|
||||
BuildMsvc(options, platform, TargetArchitecture.x64);
|
||||
var configuration = "Release";
|
||||
BuildMsvc(options, platform, TargetArchitecture.x64, configuration);
|
||||
//BuildBcl(options, platform);
|
||||
|
||||
// Export header files
|
||||
Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-dynamic.vcxproj"), "Release", "x64");
|
||||
Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "build-install.vcxproj"), "Release", "x64");
|
||||
Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-dynamic.vcxproj"), configuration, "x64");
|
||||
Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "build-install.vcxproj"), configuration, "x64");
|
||||
|
||||
// Get exported mono methods to forward them in engine module (on Win32 platforms)
|
||||
GetMonoExports(options);
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<Size>_elementsCount</Size>
|
||||
<Loop>
|
||||
<Break Condition="i == _tableSize" />
|
||||
<If Condition="_table[i]._state != 0">
|
||||
<If Condition="_table[i]._state == 2">
|
||||
<Item>_table[i]</Item>
|
||||
</If>
|
||||
<Exec>i++</Exec>
|
||||
@@ -72,7 +72,7 @@
|
||||
<Size>_elementsCount</Size>
|
||||
<Loop>
|
||||
<Break Condition="i == _tableSize" />
|
||||
<If Condition="_table[i]._state != 0">
|
||||
<If Condition="_table[i]._state == 2">
|
||||
<Item>_table[i]</Item>
|
||||
</If>
|
||||
<Exec>i++</Exec>
|
||||
|
||||
Reference in New Issue
Block a user