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

This commit is contained in:
Wojtek Figat
2024-07-19 00:32:54 +02:00
21 changed files with 347 additions and 114 deletions

View File

@@ -248,6 +248,11 @@ namespace FlaxEditor
/// </summary>
public event Action PlayModeEnd;
/// <summary>
/// Fired on Editor update
/// </summary>
public event Action EditorUpdate;
internal Editor()
{
Instance = this;
@@ -486,6 +491,8 @@ namespace FlaxEditor
{
StateMachine.CurrentState.UpdateFPS();
}
EditorUpdate?.Invoke();
// Update modules
for (int i = 0; i < _modules.Count; i++)

View File

@@ -103,9 +103,9 @@ namespace FlaxEditor.GUI
private Rectangle Button1Rect => new Rectangle(Height + ButtonsOffset, 0, ButtonsSize, ButtonsSize);
private Rectangle Button2Rect => new Rectangle(Height + ButtonsOffset, ButtonsSize, ButtonsSize, ButtonsSize);
private Rectangle Button2Rect => new Rectangle(Height + ButtonsOffset, ButtonsSize + 2, ButtonsSize, ButtonsSize);
private Rectangle Button3Rect => new Rectangle(Height + ButtonsOffset, ButtonsSize * 2, ButtonsSize, ButtonsSize);
private Rectangle Button3Rect => new Rectangle(Height + ButtonsOffset, (ButtonsSize + 2) * 2, ButtonsSize, ButtonsSize);
/// <inheritdoc />
public override void Draw()
@@ -147,6 +147,13 @@ namespace FlaxEditor.GUI
style.Foreground,
TextAlignment.Near,
TextAlignment.Center);
Render2D.DrawText(
style.FontSmall,
$"{TypeUtils.GetTypeDisplayName(Validator.AssetType.Type)}",
new Rectangle(button1Rect.Right + 2, ButtonsSize + 2, sizeForTextLeft, ButtonsSize),
style.ForegroundGrey,
TextAlignment.Near,
TextAlignment.Center);
}
}
// Check if has no item but has an asset (eg. virtual asset)
@@ -169,6 +176,13 @@ namespace FlaxEditor.GUI
style.Foreground,
TextAlignment.Near,
TextAlignment.Center);
Render2D.DrawText(
style.FontSmall,
$"{TypeUtils.GetTypeDisplayName(Validator.AssetType.Type)}",
new Rectangle(button1Rect.Right + 2, ButtonsSize + 2, sizeForTextLeft, ButtonsSize),
style.ForegroundGrey,
TextAlignment.Near,
TextAlignment.Center);
}
}
else
@@ -176,6 +190,24 @@ namespace FlaxEditor.GUI
// No element selected
Render2D.FillRectangle(iconRect, style.BackgroundNormal);
Render2D.DrawText(style.FontMedium, "No asset\nselected", iconRect, Color.Orange, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize);
float sizeForTextLeft = Width - button1Rect.Right;
if (sizeForTextLeft > 30)
{
Render2D.DrawText(
style.FontSmall,
$"None",
new Rectangle(button1Rect.Right + 2, 0, sizeForTextLeft, ButtonsSize),
style.Foreground,
TextAlignment.Near,
TextAlignment.Center);
Render2D.DrawText(
style.FontSmall,
$"{TypeUtils.GetTypeDisplayName(Validator.AssetType.Type)}",
new Rectangle(button1Rect.Right + 2, ButtonsSize + 2, sizeForTextLeft, ButtonsSize),
style.ForegroundGrey,
TextAlignment.Near,
TextAlignment.Center);
}
}
// Check if drag is over

View File

@@ -629,7 +629,7 @@ namespace FlaxEditor.GUI.Docking
internal void MoveTabRight(int index)
{
if (index < _tabs.Count - 2)
if (index < _tabs.Count - 1)
{
var tab = _tabs[index];
_tabs.RemoveAt(index);

View File

@@ -51,6 +51,7 @@ namespace FlaxEditor.GUI.Docking
public DockWindow StartDragAsyncWindow;
private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, DockPanel.DefaultHeaderHeight);
private bool IsSingleFloatingWindow => _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0;
/// <summary>
/// Initializes a new instance of the <see cref="DockPanelProxy"/> class.
@@ -187,6 +188,10 @@ namespace FlaxEditor.GUI.Docking
var headerRect = HeaderRectangle;
var tabsCount = _panel.TabsCount;
// Return and don't draw tab if only 1 window and it is floating
if (IsSingleFloatingWindow)
return;
// Check if has only one window docked
if (tabsCount == 1)
{
@@ -321,6 +326,9 @@ namespace FlaxEditor.GUI.Docking
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
if (IsSingleFloatingWindow)
return base.OnMouseDoubleClick(location, button);
// Maximize/restore on double click
var tab = GetTabAtPos(location, out _);
var rootWindow = tab?.RootWindow;
@@ -339,6 +347,8 @@ namespace FlaxEditor.GUI.Docking
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (IsSingleFloatingWindow)
return base.OnMouseDown(location, button);
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
// Check buttons
@@ -368,6 +378,9 @@ namespace FlaxEditor.GUI.Docking
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (IsSingleFloatingWindow)
return base.OnMouseUp(location, button);
// Check tabs under mouse position at the beginning and at the end
var tab = GetTabAtPos(location, out var overCross);
@@ -410,7 +423,7 @@ namespace FlaxEditor.GUI.Docking
public override void OnMouseMove(Float2 location)
{
MousePosition = location;
if (IsMouseLeftButtonDown)
if (IsMouseLeftButtonDown && !IsSingleFloatingWindow)
{
// Check if mouse is outside the header
if (!HeaderRectangle.Contains(location))
@@ -501,7 +514,10 @@ namespace FlaxEditor.GUI.Docking
/// <inheritdoc />
public override void GetDesireClientArea(out Rectangle rect)
{
rect = new Rectangle(0, DockPanel.DefaultHeaderHeight, Width, Height - DockPanel.DefaultHeaderHeight);
if (IsSingleFloatingWindow)
rect = new Rectangle(0, 0, Width, Height);
else
rect = new Rectangle(0, DockPanel.DefaultHeaderHeight, Width, Height - DockPanel.DefaultHeaderHeight);
}
private DragDropEffect TrySelectTabUnderLocation(ref Float2 location)

View File

@@ -38,7 +38,7 @@ namespace FlaxEditor.GUI
ContentItem = item;
ContentItem.AddReference(this);
Name = item.ShortName;
OnItemRenamed(item);
TooltipText = item.Path;
Height = IconSize + 4;
@@ -82,7 +82,9 @@ namespace FlaxEditor.GUI
/// <inheritdoc />
public void OnItemRenamed(ContentItem item)
{
Name = ContentItem.ShortName;
Name = item.ShortName;
if (item is ScriptItem)
Name = item.FileName; // Show extension for scripts (esp. for .h and .cpp files of the same name)
}
/// <inheritdoc />

View File

@@ -342,9 +342,10 @@ namespace FlaxEditor.Modules
{
foreach (var contentItem in items)
{
var name = contentItem.ShortName;
if (contentItem.IsAsset)
{
if (nameRegex.Match(contentItem.ShortName).Success)
if (nameRegex.Match(name).Success)
{
var asset = contentItem as AssetItem;
if (asset == null || !typeRegex.Match(asset.TypeName).Success)
@@ -358,7 +359,7 @@ namespace FlaxEditor.Modules
var splits = asset.TypeName.Split('.');
finalName = splits[splits.Length - 1];
}
matches.Add(new SearchResult { Name = asset.ShortName, Type = finalName, Item = asset });
matches.Add(new SearchResult { Name = name, Type = finalName, Item = asset });
}
}
else if (contentItem.IsFolder)
@@ -370,11 +371,12 @@ namespace FlaxEditor.Modules
}
else
{
if (nameRegex.Match(contentItem.ShortName).Success && typeRegex.Match(contentItem.GetType().Name).Success)
if (nameRegex.Match(name).Success && typeRegex.Match(contentItem.GetType().Name).Success)
{
string finalName = contentItem.GetType().Name.Replace("Item", "");
matches.Add(new SearchResult { Name = contentItem.ShortName, Type = finalName, Item = contentItem });
if (contentItem is ScriptItem)
name = contentItem.FileName; // Show extension for scripts (esp. for .h and .cpp files of the same name)
matches.Add(new SearchResult { Name = name, Type = finalName, Item = contentItem });
}
}
}

View File

@@ -21,54 +21,75 @@ namespace FlaxEditor.Windows.Assets
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
public sealed class TextureWindow : AssetEditorWindowBase<Texture>
{
private sealed class ProxyEditor : GenericEditor
/// <summary>
/// Properties base class.
/// </summary>
public class PropertiesProxyBase
{
public override void Initialize(LayoutElementsContainer layout)
internal TextureWindow _window;
/// <summary>
/// Gathers parameters from the specified texture.
/// </summary>
/// <param name="window">The asset window.</param>
public virtual void OnLoad(TextureWindow window)
{
var window = ((PropertiesProxy)Values[0])._window;
var texture = window?.Asset;
if (texture == null || !texture.IsLoaded)
{
layout.Label("Loading...", TextAlignment.Center);
return;
}
// Texture info
var general = layout.Group("General");
general.Label("Format: " + texture.Format);
general.Label(string.Format("Size: {0}x{1}", texture.Width, texture.Height)).AddCopyContextMenu();
general.Label("Mip levels: " + texture.MipLevels);
general.Label("Memory usage: " + Utilities.Utils.FormatBytesCount(texture.TotalMemoryUsage)).AddCopyContextMenu();
// Texture properties
var properties = layout.Group("Properties");
var textureGroup = new CustomValueContainer(new ScriptType(typeof(int)), texture.TextureGroup,
(instance, index) => texture.TextureGroup,
(instance, index, value) =>
{
texture.TextureGroup = (int)value;
window.MarkAsEdited();
});
properties.Property("Texture Group", textureGroup, new TextureGroupEditor(), "The texture group used by this texture.");
// Import settings
base.Initialize(layout);
// Reimport
layout.Space(10);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
// Link
_window = window;
}
/// <summary>
/// Clears temporary data.
/// </summary>
public void OnClean()
{
// Unlink
_window = null;
}
}
[CustomEditor(typeof(ProxyEditor))]
private sealed class TexturePropertiesProxy : PropertiesProxyBase
{
private sealed class ProxyEditor : GenericEditor
{
public override void Initialize(LayoutElementsContainer layout)
{
var window = ((TexturePropertiesProxy)Values[0])._window;
var texture = window?.Asset;
if (texture == null || !texture.IsLoaded)
{
layout.Label("Loading...", TextAlignment.Center);
return;
}
// Texture info
var general = layout.Group("General");
general.Label("Format: " + texture.Format);
general.Label(string.Format("Size: {0}x{1}", texture.Width, texture.Height)).AddCopyContextMenu();
general.Label("Mip levels: " + texture.MipLevels);
general.Label("Memory usage: " + Utilities.Utils.FormatBytesCount(texture.TotalMemoryUsage)).AddCopyContextMenu();
// Texture properties
var properties = layout.Group("Properties");
var textureGroup = new CustomValueContainer(new ScriptType(typeof(int)), texture.TextureGroup,
(instance, index) => texture.TextureGroup,
(instance, index, value) =>
{
texture.TextureGroup = (int)value;
window.MarkAsEdited();
});
properties.Property("Texture Group", textureGroup, new TextureGroupEditor(), "The texture group used by this texture.");
}
}
}
/// <summary>
/// The texture properties proxy object.
/// The texture import properties proxy object.
/// </summary>
[CustomEditor(typeof(ProxyEditor))]
private sealed class PropertiesProxy
private sealed class ImportPropertiesProxy : PropertiesProxyBase
{
internal TextureWindow _window;
[EditorOrder(1000), EditorDisplay("Import Settings", EditorDisplayAttribute.InlineStyle)]
public FlaxEngine.Tools.TextureTool.Options ImportSettings = new();
@@ -76,10 +97,9 @@ namespace FlaxEditor.Windows.Assets
/// Gathers parameters from the specified texture.
/// </summary>
/// <param name="window">The asset window.</param>
public void OnLoad(TextureWindow window)
public override void OnLoad(TextureWindow window)
{
// Link
_window = window;
base.OnLoad(window);
// Try to restore target asset texture import options (useful for fast reimport)
Editor.TryRestoreImportOptions(ref ImportSettings, window.Item.Path);
@@ -109,22 +129,85 @@ namespace FlaxEditor.Windows.Assets
public void DiscardChanges()
{
}
/// <summary>
/// Clears temporary data.
/// </summary>
public void OnClean()
private sealed class ProxyEditor : GenericEditor
{
// Unlink
_window = null;
public override void Initialize(LayoutElementsContainer layout)
{
// Import settings
base.Initialize(layout);
// Reimport
layout.Space(10);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxy)Values[0]).Reimport();
}
}
}
private class Tab : GUI.Tabs.Tab
{
/// <summary>
/// The presenter to use in the tab.
/// </summary>
public CustomEditorPresenter Presenter;
/// <summary>
/// The proxy to use in the tab.
/// </summary>
public PropertiesProxyBase Proxy;
public Tab(string text, TextureWindow window, bool modifiesAsset = true)
: base(text)
{
var scrollPanel = new Panel(ScrollBars.Vertical)
{
AnchorPreset = AnchorPresets.StretchAll,
Offsets = Margin.Zero,
Parent = this
};
Presenter = new CustomEditorPresenter(null);
Presenter.Panel.Parent = scrollPanel;
if (modifiesAsset)
Presenter.Modified += window.MarkAsEdited;
}
/// <inheritdoc />
public override void OnDestroy()
{
Presenter.Deselect();
Presenter = null;
Proxy = null;
base.OnDestroy();
}
}
private class TextureTab : Tab
{
public TextureTab(TextureWindow window)
: base("Texture", window)
{
Proxy = new TexturePropertiesProxy();
Presenter.Select(Proxy);
}
}
private class ImportTab : Tab
{
public ImportTab(TextureWindow window)
: base("Import", window)
{
Proxy = new ImportPropertiesProxy();
Presenter.Select(Proxy);
}
}
private readonly GUI.Tabs.Tabs _tabs;
private readonly SplitPanel _split;
private readonly TexturePreview _preview;
private readonly CustomEditorPresenter _propertiesEditor;
private readonly ToolStripButton _saveButton;
private readonly PropertiesProxy _properties;
private bool _isWaitingForLoad;
/// <inheritdoc />
@@ -145,12 +228,20 @@ namespace FlaxEditor.Windows.Assets
{
Parent = _split.Panel1
};
// Properties tabs
_tabs = new()
{
AnchorPreset = AnchorPresets.StretchAll,
Offsets = Margin.Zero,
TabsSize = new Float2(60, 20),
TabsTextHorizontalAlignment = TextAlignment.Center,
UseScroll = true,
Parent = _split.Panel2
};
// Texture properties editor
_propertiesEditor = new CustomEditorPresenter(null);
_propertiesEditor.Panel.Parent = _split.Panel2;
_properties = new PropertiesProxy();
_propertiesEditor.Select(_properties);
_tabs.AddTab(new TextureTab(this));
_tabs.AddTab(new ImportTab(this));
// Toolstrip
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
@@ -164,7 +255,11 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
protected override void UnlinkItem()
{
_properties.OnClean();
foreach (var child in _tabs.Children)
{
if (child is Tab tab && tab.Proxy != null)
tab.Proxy.OnClean();
}
_preview.Asset = null;
_isWaitingForLoad = false;
@@ -195,15 +290,6 @@ namespace FlaxEditor.Windows.Assets
base.UpdateToolstrip();
}
/// <inheritdoc />
protected override void OnClose()
{
// Discard unsaved changes
_properties.DiscardChanges();
base.OnClose();
}
/// <inheritdoc />
public override void Save()
{
@@ -231,8 +317,14 @@ namespace FlaxEditor.Windows.Assets
_isWaitingForLoad = false;
// Init properties and parameters proxy
_properties.OnLoad(this);
_propertiesEditor.BuildLayout();
foreach (var child in _tabs.Children)
{
if (child is Tab tab && tab.Proxy != null)
{
tab.Proxy.OnLoad(this);
tab.Presenter.BuildLayout();
}
}
// Setup
ClearEditedFlag();

View File

@@ -139,8 +139,8 @@ namespace FlaxEditor.Windows.Search
{
var item = items[i];
SearchItem searchItem;
if (item.Item is AssetItem assetItem)
searchItem = new AssetSearchItem(item.Name, item.Type, assetItem, this, itemsWidth, itemHeight);
if (item.Item is ContentItem contentItem)
searchItem = new ContentSearchItem(item.Name, item.Type, contentItem, this, itemsWidth, itemHeight);
else
searchItem = new SearchItem(item.Name, item.Type, item.Item, this, itemsWidth, itemHeight);
searchItem.Y = i * itemHeight;

View File

@@ -113,17 +113,17 @@ namespace FlaxEditor.Windows.Search
}
/// <summary>
/// The <see cref="SearchItem"/> for assets. Supports using asset thumbnail.
/// The <see cref="SearchItem"/> for assets. Supports using content item thumbnail.
/// </summary>
/// <seealso cref="FlaxEditor.Windows.Search.SearchItem" />
/// <seealso cref="FlaxEditor.Content.IContentItemOwner" />
internal class AssetSearchItem : SearchItem, IContentItemOwner
internal class ContentSearchItem : SearchItem, IContentItemOwner
{
private AssetItem _asset;
private FlaxEditor.GUI.ContextMenu.ContextMenu _cm;
private ContentItem _asset;
private ContextMenu _cm;
/// <inheritdoc />
public AssetSearchItem(string name, string type, AssetItem item, ContentFinder finder, float width, float height)
public ContentSearchItem(string name, string type, ContentItem item, ContentFinder finder, float width, float height)
: base(name, type, item, finder, width, height)
{
_asset = item;
@@ -136,31 +136,41 @@ namespace FlaxEditor.Windows.Search
/// <inheritdoc />
public override bool OnShowTooltip(out string text, out Float2 location, out Rectangle area)
{
if (string.IsNullOrEmpty(TooltipText) && Item is AssetItem assetItem)
if (string.IsNullOrEmpty(TooltipText) && Item is ContentItem contentItem)
{
assetItem.UpdateTooltipText();
TooltipText = assetItem.TooltipText;
contentItem.UpdateTooltipText();
TooltipText = contentItem.TooltipText;
}
return base.OnShowTooltip(out text, out location, out area);
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (base.OnMouseDown(location, button))
return true;
if (button == MouseButton.Right && Item is ContentItem)
return true;
return false;
}
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (base.OnMouseUp(location, button))
return true;
if (button == MouseButton.Right && Item is AssetItem assetItem)
if (button == MouseButton.Right && Item is ContentItem contentItem)
{
// Show context menu
var proxy = Editor.Instance.ContentDatabase.GetProxy(assetItem);
var proxy = Editor.Instance.ContentDatabase.GetProxy(contentItem);
ContextMenuButton b;
var cm = new FlaxEditor.GUI.ContextMenu.ContextMenu { Tag = assetItem };
var cm = new ContextMenu { Tag = contentItem };
b = cm.AddButton("Open", () => Editor.Instance.ContentFinding.Open(Item));
cm.AddSeparator();
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(assetItem.Path)));
cm.AddButton("Show in Content window", () => Editor.Instance.Windows.ContentWin.Select(assetItem, true));
b.Enabled = proxy != null && proxy.CanReimport(assetItem);
if (assetItem is BinaryAssetItem binaryAsset)
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(contentItem.Path)));
cm.AddButton("Show in Content window", () => Editor.Instance.Windows.ContentWin.Select(contentItem, true));
b.Enabled = proxy != null && proxy.CanReimport(contentItem);
if (contentItem is BinaryAssetItem binaryAsset)
{
if (!binaryAsset.GetImportPath(out string importPath))
{
@@ -172,14 +182,17 @@ namespace FlaxEditor.Windows.Search
}
}
cm.AddSeparator();
cm.AddButton("Copy asset ID", () => Clipboard.Text = FlaxEngine.Json.JsonSerializer.GetStringID(assetItem.ID));
cm.AddButton("Select actors using this asset", () => Editor.Instance.SceneEditing.SelectActorsUsingAsset(assetItem.ID));
cm.AddButton("Show asset references graph", () => Editor.Instance.Windows.Open(new AssetReferencesGraphWindow(Editor.Instance, assetItem)));
cm.AddSeparator();
proxy?.OnContentWindowContextMenu(cm, assetItem);
assetItem.OnContextMenu(cm);
cm.AddButton("Copy name to Clipboard", () => Clipboard.Text = assetItem.NamePath);
cm.AddButton("Copy path to Clipboard", () => Clipboard.Text = assetItem.Path);
if (contentItem is AssetItem assetItem)
{
cm.AddButton("Copy asset ID", () => Clipboard.Text = FlaxEngine.Json.JsonSerializer.GetStringID(assetItem.ID));
cm.AddButton("Select actors using this asset", () => Editor.Instance.SceneEditing.SelectActorsUsingAsset(assetItem.ID));
cm.AddButton("Show asset references graph", () => Editor.Instance.Windows.Open(new AssetReferencesGraphWindow(Editor.Instance, assetItem)));
cm.AddButton("Copy name to Clipboard", () => Clipboard.Text = assetItem.NamePath);
cm.AddButton("Copy path to Clipboard", () => Clipboard.Text = assetItem.Path);
cm.AddSeparator();
}
proxy?.OnContentWindowContextMenu(cm, contentItem);
contentItem.OnContextMenu(cm);
cm.Show(this, location);
_cm = cm;
return true;

View File

@@ -271,8 +271,17 @@ public:
public:
/// <summary>
/// Determines whether this audio source started playing audio via audio backend. After audio play it may wait for audio clip data to be loaded or streamed.
/// [Deprecated in v1.9]
/// </summary>
API_PROPERTY() FORCE_INLINE bool IsActuallyPlayingSth() const
API_PROPERTY() DEPRECATED FORCE_INLINE bool IsActuallyPlayingSth() const
{
return _isActuallyPlayingSth;
}
/// <summary>
/// Determines whether this audio source started playing audio via audio backend. After audio play it may wait for audio clip data to be loaded or streamed.
/// </summary>
API_PROPERTY() FORCE_INLINE bool IsActuallyPlaying() const
{
return _isActuallyPlayingSth;
}

View File

@@ -14,6 +14,17 @@ namespace FlaxEngine
/// </summary>
public object Instance => _instance ?? (_instance = CreateInstance());
/// <summary>
/// Gets the instance of the serialized object from the json data. Cached internally.
/// </summary>
/// <returns>The asset instance object or null.</returns>
public T GetInstance<T>()
{
if (Instance is T instance)
return instance;
return default;
}
/// <summary>
/// Creates a new instance of the serialized object from the json asset data.
/// </summary>

View File

@@ -33,6 +33,15 @@ namespace FlaxEngine
Asset = asset;
}
/// <summary>
/// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type.
/// </summary>
/// <returns>The asset instance object or null.</returns>
public U GetInstance<U>()
{
return Asset ? Asset.GetInstance<U>() : default(U);
}
/// <summary>
/// Implicit cast operator.
/// </summary>

View File

@@ -18,7 +18,7 @@ namespace FlaxEditor.Content.Settings
/// <summary>
/// The layers names.
/// </summary>
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = true, Display = CollectionAttribute.DisplayType.Inline)]
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)]
public string[] Layers = new string[32];
/// <summary>

View File

@@ -294,6 +294,10 @@ Quaternion Quaternion::FromDirection(const Float3& direction)
{
RotationAxis(Float3::Left, PI_OVER_2, orientation);
}
else if (Float3::Dot(direction, Float3::Down) >= 0.999f)
{
RotationAxis(Float3::Right, PI_OVER_2, orientation);
}
else
{
Float3 right, up;

View File

@@ -654,6 +654,10 @@ namespace FlaxEngine
{
orientation = RotationAxis(Float3.Left, Mathf.PiOverTwo);
}
else if (Float3.Dot(direction, Float3.Down) >= 0.999f)
{
orientation = RotationAxis(Float3.Right, Mathf.PiOverTwo);
}
else
{
var right = Float3.Cross(direction, Float3.Up);

View File

@@ -19,6 +19,7 @@ CharacterController::CharacterController(const SpawnParams& params)
, _minMoveDistance(0.0f)
, _isUpdatingTransform(false)
, _upDirection(Vector3::Up)
, _gravityDisplacement(Vector3::Zero)
, _nonWalkableMode(NonWalkableModes::PreventClimbing)
, _lastFlags(CollisionFlags::None)
{
@@ -148,10 +149,16 @@ CharacterController::CollisionFlags CharacterController::GetFlags() const
CharacterController::CollisionFlags CharacterController::SimpleMove(const Vector3& speed)
{
const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds();
Vector3 displacement = speed;
displacement += GetPhysicsScene()->GetGravity() * deltaTime;
displacement *= deltaTime;
return Move(displacement);
Vector3 displacement = speed + _gravityDisplacement;
CollisionFlags result = Move(displacement * deltaTime);
if ((static_cast<int>(result) & static_cast<int>(CollisionFlags::Below)) != 0)
{
// Reset accumulated gravity acceleration when we touch the ground
_gravityDisplacement = Vector3::Zero;
}
else
_gravityDisplacement += GetPhysicsScene()->GetGravity() * deltaTime;
return result;
}
CharacterController::CollisionFlags CharacterController::Move(const Vector3& displacement)

View File

@@ -66,6 +66,7 @@ private:
float _minMoveDistance;
bool _isUpdatingTransform;
Vector3 _upDirection;
Vector3 _gravityDisplacement;
NonWalkableModes _nonWalkableMode;
CollisionFlags _lastFlags;

View File

@@ -133,7 +133,13 @@ void MeshCollider::GetGeometry(CollisionShape& collision)
// Prepare scale
Float3 scale = _cachedScale;
const float minSize = 0.001f;
scale = Float3::Max(scale.GetAbsolute(), minSize);
Float3 scaleAbs = scale.GetAbsolute();
if (scaleAbs.X < minSize)
scale.X = Math::Sign(scale.X) * minSize;
if (scaleAbs.Y < minSize)
scale.Y = Math::Sign(scale.Y) * minSize;
if (scaleAbs.Z < minSize)
scale.Z = Math::Sign(scale.Z) * minSize;
// Setup shape (based on type)
CollisionDataType type = CollisionDataType::None;

View File

@@ -321,7 +321,12 @@ class CharacterControllerHitReportPhysX : public PxUserControllerHitReport
{
void onHit(const PxControllerHit& hit, Collision& c)
{
ASSERT_LOW_LAYER(c.ThisActor && c.OtherActor);
if (c.ThisActor == nullptr || c.OtherActor == nullptr)
{
// One of the actors was deleted (eg. via RigidBody destroyed by gameplay) then skip processing this collision
return;
}
c.Impulse = Vector3::Zero;
c.ThisVelocity = P2C(hit.dir) * hit.length;
c.OtherVelocity = Vector3::Zero;
@@ -564,6 +569,7 @@ namespace
Array<PxBase*> DeleteObjects;
bool _queriesHitTriggers = true;
bool _enableCCD = true;
PhysicsCombineMode _frictionCombineMode = PhysicsCombineMode::Average;
PhysicsCombineMode _restitutionCombineMode = PhysicsCombineMode::Average;
@@ -697,6 +703,8 @@ PxFilterFlags FilterShader(
pairFlags |= PxPairFlag::eNOTIFY_TOUCH_LOST;
pairFlags |= PxPairFlag::ePOST_SOLVER_VELOCITY;
pairFlags |= PxPairFlag::eNOTIFY_CONTACT_POINTS;
if (_enableCCD)
pairFlags |= PxPairFlag::eDETECT_CCD_CONTACT;
return PxFilterFlag::eDEFAULT;
}
@@ -1220,6 +1228,7 @@ void PhysicsBackend::Shutdown()
void PhysicsBackend::ApplySettings(const PhysicsSettings& settings)
{
_queriesHitTriggers = settings.QueriesHitTriggers;
_enableCCD = !settings.DisableCCD;
_frictionCombineMode = settings.FrictionCombineMode;
_restitutionCombineMode = settings.RestitutionCombineMode;
@@ -1256,6 +1265,8 @@ void* PhysicsBackend::CreateScene(const PhysicsSettings& settings)
sceneDesc.simulationEventCallback = &scenePhysX->EventsCallback;
sceneDesc.filterShader = FilterShader;
sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity;
if (settings.EnableEnhancedDeterminism)
sceneDesc.flags |= PxSceneFlag::eENABLE_ENHANCED_DETERMINISM;
switch (settings.SolverType)
{
case PhysicsSolverType::ProjectedGaussSeidelIterativeSolver:

View File

@@ -94,10 +94,16 @@ public:
API_FIELD(Attributes="EditorOrder(71), EditorDisplay(\"Simulation\")")
PhysicsBroadPhaseType BroadPhaseType = PhysicsBroadPhaseType::ParallelAutomaticBoxPruning;
/// <summary>
/// Enables enhanced determinism in the simulation. This has a performance impact.
/// </summary>
API_FIELD(Attributes="EditorOrder(71), EditorDisplay(\"Simulation\")")
bool EnableEnhancedDeterminism = false;
/// <summary>
/// The solver type to use in the simulation.
/// </summary>
API_FIELD(Attributes="EditorOrder(72), EditorDisplay(\"Simulation\")")
API_FIELD(Attributes="EditorOrder(73), EditorDisplay(\"Simulation\")")
PhysicsSolverType SolverType = PhysicsSolverType::ProjectedGaussSeidelIterativeSolver;
/// <summary>

View File

@@ -130,6 +130,7 @@ namespace FlaxEngine.GUI
{
// Clear flag
_splitterClicked = false;
PerformLayout();
// End capturing mouse
EndMouseCapture();