Merge branch 'master' into 1.1

# Conflicts:
#	Source/Engine/Content/JsonAsset.h
#	Source/Engine/Core/Config/Settings.h
This commit is contained in:
Wojtek Figat
2021-02-15 10:40:59 +01:00
77 changed files with 1111 additions and 531 deletions

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 />

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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)

View File

@@ -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),
}
},
};
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -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 '-'

View File

@@ -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)
{

View File

@@ -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()
{

View File

@@ -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();

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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()

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Config/Settings.h"
#include "Engine/Serialization/Json.h"
/// <summary>
/// Layers and objects tags settings.

View File

@@ -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>

View File

@@ -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)
{

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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)
{
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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; \

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -256,10 +256,8 @@ void ParticleEmitterGPUGenerator::ProcessGroupTextures(Box* box, Node* node, Val
}
// Scene Depth
case 8:
{
sampleSceneDepth(node, value, box);
break;
}
// Texture
case 11:
{

View File

@@ -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

View File

@@ -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)

View File

@@ -284,6 +284,7 @@ protected:
Vector2 _trackingMouseOffset;
bool _isUsingMouseOffset;
Rectangle _mouseOffsetScreenSize;
bool _isTrackingMouse;
explicit WindowBase(const CreateWindowSettings& settings);

View File

@@ -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()

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());
}

View File

@@ -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);

View File

@@ -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];

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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()

View File

@@ -205,6 +205,7 @@ public:
static MaterialValue getUVs;
static MaterialValue getTime;
static MaterialValue getNormal;
static MaterialValue getNormalZero;
static MaterialValue getVertexColor;
static MaterialGraphBoxesMapping MaterialGraphBoxesMappings[];

View File

@@ -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;

View File

@@ -942,6 +942,8 @@ namespace FlaxEngine.GUI
{
base.OnLostFocus();
if (IsReadOnly)
return;
OnEditEnd();
}

View File

@@ -1006,10 +1006,9 @@ namespace FlaxEngine.GUI
c = c.Parent;
if (c == parent)
return location;
break;
}
throw new ArgumentException();
return location;
}
/// <summary>

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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;

View File

@@ -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().

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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>