Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be5e13301e | |||
| f41e32683a | |||
| 1a8827ba76 | |||
| eed227aa79 | |||
| 27ee42b0a1 | |||
| fd8ae9bc2b | |||
| e0f234c667 | |||
| bdeb89538c | |||
| 0f86537099 | |||
| f6f7bbb3d0 | |||
| 422300adbd | |||
| 89a1f00c57 | |||
| f4be035f04 | |||
| c2ec3fe2cb | |||
| fca6ed43cc | |||
| 84ccb9df0c | |||
| 777602fee6 | |||
| 24654e5b02 | |||
| 44117084c8 | |||
| 2531a4b918 | |||
| 9ce6026192 | |||
| a12c5e2203 | |||
| ff526ecafb | |||
| 1badeda31c | |||
| 018c7cf33d | |||
| 6daec81db1 | |||
| c36c39df37 | |||
| b7a59447a3 | |||
| 6fa38b75d5 | |||
| de1b515f1e | |||
| b004e90606 | |||
| b7185bfe72 | |||
| 7a41e7d6d8 | |||
| 3e670f5e80 | |||
| 7e19ffbe40 | |||
| 80e20744aa | |||
| 16effd8328 | |||
| e7c5f257e9 | |||
| 320d37d9a2 | |||
| 791fb785cf | |||
| ab6b5927f8 | |||
| 7c1df5c980 | |||
| 14ad3f892e | |||
| bd3129e9e7 | |||
| 45cc04d434 | |||
| f78954c174 | |||
| e43a12a9a8 | |||
| 1e6fdb7f02 | |||
| 067c8ae5b8 | |||
| 95aa04c334 | |||
| efd6bc27b8 | |||
| 3140c711a4 | |||
| 848e9c92aa | |||
| 6dad8259d6 | |||
| 9f72c465fe | |||
| 285762bfdb | |||
| 571821bf3d | |||
| 96a081bf93 | |||
| 637f3dc176 | |||
| 5010597bd2 | |||
| 4d4d1a589f | |||
| 1176dc515d | |||
| 96d670efb7 | |||
| 00e4b09e7e | |||
| ec73cc6b0d | |||
| 3542f20787 | |||
| 43511a96b0 | |||
| 97bcdacd9a | |||
| 253442abd1 | |||
| c8912ad100 | |||
| 40413edbab | |||
| c10cfc8e45 | |||
| 5739c0bef4 | |||
| 4f97225c46 | |||
| 2c8da4ea04 | |||
| d697bd7402 | |||
| 0bcc01c3c7 | |||
| de76d3623e | |||
| 33caae6935 | |||
| 75d9e36bd2 | |||
| 72f2c8f5cd | |||
| 9cfb3dd220 | |||
| 62d6658444 | |||
| 66818802b1 | |||
| cc4fdf5cc2 | |||
| fde40949b2 | |||
| 65c1a8258e | |||
| 6e89f152ef | |||
| e71f43ea79 | |||
| d33ebc3105 | |||
| 0de61ba218 | |||
| 283a3e6bf9 | |||
| 8281e743cd | |||
| ba48b2e4f3 | |||
| 64708a14d9 | |||
| af3836d611 | |||
| a421effd1b | |||
| 811bf0d630 | |||
| 94e529e801 | |||
| a7c9eff959 | |||
| a831e15bf7 | |||
| d77e1e9a53 | |||
| 699fb12604 | |||
| 9505be310f | |||
| 503a0e6763 | |||
| 65b35a4b8a | |||
| c2e8e492d7 | |||
| c916fb1844 | |||
| 004339b81e |
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Redirect to our own Git LFS server
|
||||
[lfs]
|
||||
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
url="https://git.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
locksverify = false
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+1
-1
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 12,
|
||||
"Revision": 0,
|
||||
"Build": 6912
|
||||
"Build": 6914
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2026 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace FlaxEngine.Tools
|
||||
{
|
||||
partial struct Options
|
||||
{
|
||||
private bool ShowBtiDepth => Format != AudioFormat.Vorbis;
|
||||
private bool ShowBitDepth => Format != AudioFormat.Vorbis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace FlaxEngine.Tools
|
||||
private bool ShowRootMotion => ShowAnimation && RootMotion != RootMotionMode.None;
|
||||
private bool ShowSmoothingNormalsAngle => ShowGeometry && CalculateNormals;
|
||||
private bool ShowSmoothingTangentsAngle => ShowGeometry && CalculateTangents;
|
||||
private bool ShowGenerateLODs => ShowGeometry && GenerateLODs;
|
||||
private bool ShowFramesRange => ShowAnimation && Duration == AnimationDuration.Custom;
|
||||
private bool ShowSplitting => Type != ModelType.Prefab;
|
||||
}
|
||||
|
||||
@@ -98,12 +98,12 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the asset (if it's loaded).
|
||||
/// Reloads the asset (if it's loaded or failed to load).
|
||||
/// </summary>
|
||||
public void Reload()
|
||||
{
|
||||
var asset = FlaxEngine.Content.GetAsset(ID);
|
||||
if (asset != null && asset.IsLoaded)
|
||||
if (asset != null && (asset.IsLoaded || asset.LastLoadFailed))
|
||||
{
|
||||
asset.Reload();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class for <see cref="MaterialBase"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
public abstract class MaterialBaseProxy : BinaryAssetProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The material preview drawer.
|
||||
/// </summary>
|
||||
protected MaterialPreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
|
||||
{
|
||||
base.OnContentWindowContextMenu(menu, item);
|
||||
|
||||
if (item is BinaryAssetItem binaryAssetItem)
|
||||
{
|
||||
var button = menu.AddButton("Create Material Instance", CreateMaterialInstanceClicked);
|
||||
button.Tag = binaryAssetItem;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMaterialInstanceClicked(ContextMenuButton button)
|
||||
{
|
||||
var binaryAssetItem = (BinaryAssetItem)button.Tag;
|
||||
CreateMaterialInstance(binaryAssetItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the material instance from the given material.
|
||||
/// </summary>
|
||||
/// <param name="materialItem">The material item to use as a base material.</param>
|
||||
public static void CreateMaterialInstance(BinaryAssetItem materialItem)
|
||||
{
|
||||
var materialInstanceName = materialItem.ShortName + " Instance";
|
||||
var materialInstanceProxy = Editor.Instance.ContentDatabase.GetProxy<MaterialInstance>();
|
||||
Editor.Instance.Windows.ContentWin.NewItem(materialInstanceProxy, null, item => OnMaterialInstanceCreated(item, materialItem), materialInstanceName);
|
||||
}
|
||||
|
||||
private static void OnMaterialInstanceCreated(ContentItem item, BinaryAssetItem materialItem)
|
||||
{
|
||||
var assetItem = (AssetItem)item;
|
||||
var materialInstance = FlaxEngine.Content.LoadAsync<MaterialInstance>(assetItem.ID);
|
||||
if (materialInstance == null || materialInstance.WaitForLoaded())
|
||||
{
|
||||
Editor.LogError("Failed to load created material instance.");
|
||||
return;
|
||||
}
|
||||
materialInstance.BaseMaterial = FlaxEngine.Content.LoadAsync<MaterialBase>(materialItem.ID);
|
||||
materialInstance.Save();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new MaterialPreview(false);
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Material = (MaterialBase)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Material = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,23 +2,18 @@
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="MaterialInstance"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
[ContentContextMenu("New/Material/Material Instance")]
|
||||
public class MaterialInstanceProxy : BinaryAssetProxy
|
||||
public class MaterialInstanceProxy : MaterialBaseProxy
|
||||
{
|
||||
private MaterialPreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Material Instance";
|
||||
|
||||
@@ -34,12 +29,6 @@ namespace FlaxEditor.Content
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(MaterialInstance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
@@ -47,49 +36,10 @@ namespace FlaxEditor.Content
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new MaterialPreview(false);
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((MaterialInstance)request.Asset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Material = (MaterialInstance)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Material = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,18 @@
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Thumbnails;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="Material"/> asset proxy object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||
[ContentContextMenu("New/Material/Material")]
|
||||
public class MaterialProxy : BinaryAssetProxy
|
||||
public class MaterialProxy : MaterialBaseProxy
|
||||
{
|
||||
private MaterialPreview _preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Material";
|
||||
|
||||
@@ -35,12 +29,6 @@ namespace FlaxEditor.Content
|
||||
/// <inheritdoc />
|
||||
public override Type AssetType => typeof(Material);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
return targetLocation.CanHaveAssets;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(string outputPath, object arg)
|
||||
{
|
||||
@@ -48,92 +36,10 @@ namespace FlaxEditor.Content
|
||||
throw new Exception("Failed to create new asset.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
|
||||
{
|
||||
base.OnContentWindowContextMenu(menu, item);
|
||||
|
||||
if (item is BinaryAssetItem binaryAssetItem)
|
||||
{
|
||||
var button = menu.AddButton("Create Material Instance", CreateMaterialInstanceClicked);
|
||||
button.Tag = binaryAssetItem;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMaterialInstanceClicked(ContextMenuButton obj)
|
||||
{
|
||||
var binaryAssetItem = (BinaryAssetItem)obj.Tag;
|
||||
CreateMaterialInstance(binaryAssetItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the material instance from the given material.
|
||||
/// </summary>
|
||||
/// <param name="materialItem">The material item to use as a base material.</param>
|
||||
public static void CreateMaterialInstance(BinaryAssetItem materialItem)
|
||||
{
|
||||
var materialInstanceName = materialItem.ShortName + " Instance";
|
||||
var materialInstanceProxy = Editor.Instance.ContentDatabase.GetProxy<MaterialInstance>();
|
||||
Editor.Instance.Windows.ContentWin.NewItem(materialInstanceProxy, null, item => OnMaterialInstanceCreated(item, materialItem), materialInstanceName);
|
||||
}
|
||||
|
||||
private static void OnMaterialInstanceCreated(ContentItem item, BinaryAssetItem materialItem)
|
||||
{
|
||||
var assetItem = (AssetItem)item;
|
||||
var materialInstance = FlaxEngine.Content.LoadAsync<MaterialInstance>(assetItem.ID);
|
||||
if (materialInstance == null || materialInstance.WaitForLoaded())
|
||||
{
|
||||
Editor.LogError("Failed to load created material instance.");
|
||||
return;
|
||||
}
|
||||
|
||||
materialInstance.BaseMaterial = FlaxEngine.Content.LoadAsync<Material>(materialItem.ID);
|
||||
materialInstance.Save();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new MaterialPreview(false);
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Material)request.Asset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||
{
|
||||
_preview.Material = (Material)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
|
||||
{
|
||||
_preview.Material = null;
|
||||
_preview.Parent = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_preview != null)
|
||||
{
|
||||
_preview.Dispose();
|
||||
_preview = null;
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ bool WebPlatformTools::OnPostProcess(CookingData& data)
|
||||
FileSystem::GetChildDirectories(pythons, emscriptenSdk / TEXT("/python"));
|
||||
if (pythons.HasItems())
|
||||
{
|
||||
procSettings.Arguments = procSettings.FileName + TEXT(".py ") + procSettings.Arguments;
|
||||
procSettings.Arguments = String::Format(TEXT("\"{}.py\" {}"), procSettings.FileName, procSettings.Arguments);
|
||||
#if PLATFORM_WINDOWS
|
||||
procSettings.FileName = pythons[0] / TEXT("/python.exe");
|
||||
#else
|
||||
|
||||
@@ -343,6 +343,7 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
|
||||
private bool _buildOnUpdate;
|
||||
private bool _initialized;
|
||||
private bool _readOnly;
|
||||
|
||||
/// <summary>
|
||||
@@ -430,6 +431,7 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
ClearLayout();
|
||||
_buildOnUpdate = false;
|
||||
_initialized = true;
|
||||
Editor.Setup(this);
|
||||
|
||||
Panel.IsLayoutLocked = false;
|
||||
@@ -506,7 +508,11 @@ namespace FlaxEditor.CustomEditors
|
||||
/// </summary>
|
||||
protected virtual void OnSelectionChanged()
|
||||
{
|
||||
BuildLayout();
|
||||
// Defer building the layout after we have initialized to improve initial loading times
|
||||
if (!_initialized)
|
||||
_buildOnUpdate = true;
|
||||
else
|
||||
BuildLayout();
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
if (_previouslyFocused != null)
|
||||
{
|
||||
_previouslyFocused.RootWindow?.Focus();
|
||||
_previouslyFocused.Focus();
|
||||
_previouslyFocused?.Focus();
|
||||
_previouslyFocused = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -589,6 +589,8 @@ namespace FlaxEditor.GUI
|
||||
// Get the next item
|
||||
bool controlDown = Root.GetKey(KeyboardKeys.Control);
|
||||
var items = GetVisibleItems(!controlDown);
|
||||
if (items.Count == 0)
|
||||
return true;
|
||||
var focusedIndex = items.IndexOf(focusedItem);
|
||||
|
||||
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
|
||||
|
||||
@@ -229,6 +229,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
private List<Track> _mediaMoveStartTracks;
|
||||
private byte[][] _mediaMoveStartData;
|
||||
private float _zoom = 1.0f;
|
||||
private float _tracksVScrollTarget;
|
||||
private bool _isMovingPositionHandle;
|
||||
private bool _canPlayPause = true, _canStop = true;
|
||||
private List<IUndoAction> _batchedUndoActions;
|
||||
@@ -1301,10 +1302,13 @@ namespace FlaxEditor.GUI.Timeline
|
||||
if (track.ParentTrack != null)
|
||||
OnTracksOrderChanged();
|
||||
track.OnSpawned();
|
||||
_tracksPanelArea.ScrollViewTo(track);
|
||||
MarkAsEdited();
|
||||
if (withUndo)
|
||||
Undo?.AddAction(new AddRemoveTrackAction(this, track, true));
|
||||
|
||||
// Scroll to track
|
||||
_tracksPanelArea.ScrollViewTo(track);
|
||||
_tracksVScrollTarget = _tracksPanelArea.VScrollBar.TargetValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2033,12 +2037,24 @@ namespace FlaxEditor.GUI.Timeline
|
||||
base.Update(deltaTime);
|
||||
|
||||
// Synchronize scroll vertical bars for tracks and media panels to keep the view in sync
|
||||
var scroll1 = _tracksPanelArea.VScrollBar;
|
||||
var scroll2 = _backgroundArea.VScrollBar;
|
||||
if (scroll1.IsThumbClicked || _tracksPanelArea.IsMouseOver)
|
||||
scroll2.TargetValue = scroll1.Value;
|
||||
var tracksVScroll = _tracksPanelArea.VScrollBar;
|
||||
var backgroundVScroll = _backgroundArea.VScrollBar;
|
||||
bool forceBackgroundToTracksScroll = _tracksVScrollTarget > 0;
|
||||
if (forceBackgroundToTracksScroll)
|
||||
{
|
||||
backgroundVScroll.TargetValue = tracksVScroll.Value;
|
||||
|
||||
if (Mathf.Abs(tracksVScroll.Value - _tracksVScrollTarget) < 0.5f)
|
||||
_tracksVScrollTarget = 0f;
|
||||
}
|
||||
else if (tracksVScroll.IsThumbClicked || _tracksPanelArea.IsMouseOver)
|
||||
{
|
||||
backgroundVScroll.TargetValue = tracksVScroll.Value;
|
||||
}
|
||||
else
|
||||
scroll1.TargetValue = scroll2.Value;
|
||||
{
|
||||
tracksVScroll.TargetValue = backgroundVScroll.Value;
|
||||
}
|
||||
|
||||
// Batch undo actions
|
||||
if (_batchedUndoActions != null && _batchedUndoActions.Count != 0)
|
||||
|
||||
@@ -181,7 +181,7 @@ public class WindowDecorations : ContainerControl
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
var dpiScale = _window.DpiScale;
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is DPI adjusted in window space
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var winSize = _window.Size;
|
||||
@@ -214,6 +214,7 @@ public class WindowDecorations : ContainerControl
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
pos /= dpiScale; // The position should not be DPI adjusted in control space
|
||||
var controlUnderMouse = GetChildAt(pos, control => control != _title);
|
||||
if (_title.Bounds.Contains(pos) && controlUnderMouse == null)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
@@ -36,8 +36,10 @@ internal class DirectionGizmo : ContainerControl
|
||||
private List<AxisData> _axisData = new List<AxisData>();
|
||||
private int _hoveredAxisIndex = -1;
|
||||
|
||||
private bool _mouseDown;
|
||||
private Float2 _mouseDownLocation;
|
||||
|
||||
private SpriteHandle _posHandle;
|
||||
private SpriteHandle _negHandle;
|
||||
|
||||
private FontReference _fontReference;
|
||||
|
||||
@@ -110,7 +112,6 @@ internal class DirectionGizmo : ContainerControl
|
||||
|
||||
var editor = Editor.Instance;
|
||||
_posHandle = editor.Icons.VisjectBoxClosed32;
|
||||
_negHandle = editor.Icons.VisjectBoxOpen32;
|
||||
|
||||
_fontReference = new FontReference(Style.Current.FontSmall);
|
||||
|
||||
@@ -142,7 +143,25 @@ internal class DirectionGizmo : ContainerControl
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
_hoveredAxisIndex = -1;
|
||||
|
||||
if (_mouseDown)
|
||||
{
|
||||
StartMouseCapture(true);
|
||||
Cursor = CursorType.Hidden;
|
||||
|
||||
const float sensitivity = 0.125f;
|
||||
Float2 delta = Input.MousePositionDelta;
|
||||
delta *= Mathf.DegreesToRadians;
|
||||
delta *= sensitivity;
|
||||
|
||||
const float orbitRadius = 500f;
|
||||
Quaternion newOrientation = _viewport.ViewOrientation * Quaternion.RotationYawPitchRoll(delta.X , delta.Y, 0f);
|
||||
Vector3 orbitCenter = _viewport.ViewPosition + _viewport.ViewDirection * orbitRadius;
|
||||
_viewport.ViewportCamera.SetArcBallView(newOrientation, orbitCenter, orbitRadius);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check which axis is being hovered - check from closest to farthest for proper layering
|
||||
for (int i = _spritePositions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -156,9 +175,30 @@ internal class DirectionGizmo : ContainerControl
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
_mouseDown = true;
|
||||
_mouseDownLocation = location;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (_mouseDown)
|
||||
{
|
||||
_mouseDown = false;
|
||||
|
||||
if (_mouseDownLocation != location)
|
||||
{
|
||||
_mouseDown = false;
|
||||
EndMouseCapture();
|
||||
Root.MousePosition = PointToParent(Root, _mouseDownLocation);
|
||||
Cursor = CursorType.Default;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (base.OnMouseUp(location, button))
|
||||
return true;
|
||||
|
||||
@@ -222,7 +262,10 @@ internal class DirectionGizmo : ContainerControl
|
||||
else
|
||||
{
|
||||
// This could be some actual math expression, not that hack
|
||||
var fov = _owner.Viewport.FieldOfView / 60.0f;
|
||||
float fov = _owner.Viewport.FieldOfView;
|
||||
if (_owner.Viewport.ViewportCamera is Viewport.Cameras.FPSCamera fpsCam)
|
||||
fov += fpsCam.AdditionalZoomFOV;
|
||||
fov /= 60.0f;
|
||||
float scaleAt30 = 0.1f, scaleAt60 = 1.0f, scaleAt120 = 1.5f, scaleAt180 = 3.0f;
|
||||
heightNormalization /= Mathf.Lerp(scaleAt30, scaleAt60, fov);
|
||||
heightNormalization /= Mathf.Lerp(scaleAt60, scaleAt120, Mathf.Saturate(fov - 1));
|
||||
@@ -266,7 +309,12 @@ internal class DirectionGizmo : ContainerControl
|
||||
// Rebuild sprite positions list for hover detection
|
||||
_spritePositions.Clear();
|
||||
|
||||
Render2D.DrawSprite(_posHandle, new Rectangle(0, 0, Size), Color.Black.AlphaMultiplied(_backgroundOpacity));
|
||||
if (IsMouseOver)
|
||||
{
|
||||
Rectangle backgroundRect = new Rectangle(0, 0, Size);
|
||||
Color backgroundColor = Color.DarkGray.AlphaMultiplied(_backgroundOpacity);
|
||||
Render2D.DrawSprite(_posHandle, backgroundRect, backgroundColor);
|
||||
}
|
||||
|
||||
// Draw in order from farthest to closest
|
||||
for (int i = 0; i < _axisData.Count; i++)
|
||||
|
||||
@@ -2,12 +2,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
@@ -21,7 +17,6 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
{
|
||||
private Dictionary<ScriptType, string> _typeCache = new Dictionary<ScriptType, string>();
|
||||
private Dictionary<ScriptMemberInfo, string> _memberCache = new Dictionary<ScriptMemberInfo, string>();
|
||||
private Dictionary<Assembly, Dictionary<string, string>> _xmlCache = new Dictionary<Assembly, Dictionary<string, string>>();
|
||||
|
||||
internal CodeDocsModule(Editor editor)
|
||||
: base(editor)
|
||||
@@ -61,13 +56,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
else if (type.Type != null)
|
||||
{
|
||||
// Try to use xml docs for managed type
|
||||
var xml = GetXmlDocs(type.Type.Assembly);
|
||||
if (xml != null)
|
||||
{
|
||||
var key = "T:" + GetXmlKey(type.Type.FullName);
|
||||
if (xml.TryGetValue(key, out var xmlDoc))
|
||||
text += '\n' + FilterWhitespaces(xmlDoc);
|
||||
}
|
||||
var xmlDoc = DebugCommands.GetXml(type.Type);
|
||||
if (xmlDoc != null)
|
||||
text += '\n' + xmlDoc;
|
||||
}
|
||||
|
||||
_typeCache.Add(type, text);
|
||||
@@ -108,242 +99,20 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
else if (member.Type != null)
|
||||
{
|
||||
// Try to use xml docs for managed member
|
||||
var memberInfo = member.Type;
|
||||
var xml = GetXmlDocs(memberInfo.DeclaringType.Assembly);
|
||||
if (xml != null)
|
||||
{
|
||||
// [Reference: MSDN Magazine, October 2019, Volume 34 Number 10, "Accessing XML Documentation via Reflection"]
|
||||
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/october/csharp-accessing-xml-documentation-via-reflection
|
||||
var memberType = memberInfo.MemberType;
|
||||
string key = null;
|
||||
if (memberType.HasFlag(MemberTypes.Field))
|
||||
{
|
||||
var fieldInfo = (FieldInfo)memberInfo;
|
||||
key = "F:" + GetXmlKey(fieldInfo.DeclaringType.FullName) + "." + fieldInfo.Name;
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.Property))
|
||||
{
|
||||
var propertyInfo = (PropertyInfo)memberInfo;
|
||||
key = "P:" + GetXmlKey(propertyInfo.DeclaringType.FullName) + "." + propertyInfo.Name;
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.Event))
|
||||
{
|
||||
var eventInfo = (EventInfo)memberInfo;
|
||||
key = "E:" + GetXmlKey(eventInfo.DeclaringType.FullName) + "." + eventInfo.Name;
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.Constructor))
|
||||
{
|
||||
var constructorInfo = (ConstructorInfo)memberInfo;
|
||||
key = GetXmlKey(constructorInfo);
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.Method))
|
||||
{
|
||||
var methodInfo = (MethodInfo)memberInfo;
|
||||
key = GetXmlKey(methodInfo);
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.TypeInfo) || memberType.HasFlag(MemberTypes.NestedType))
|
||||
{
|
||||
var typeInfo = (TypeInfo)memberInfo;
|
||||
key = "T:" + GetXmlKey(typeInfo.FullName);
|
||||
}
|
||||
if (key != null)
|
||||
xml.TryGetValue(key, out text);
|
||||
|
||||
// Customize tooltips for properties to be more human-readable in UI
|
||||
if (text != null && memberType.HasFlag(MemberTypes.Property) && text.StartsWith("Gets or sets ", StringComparison.Ordinal))
|
||||
{
|
||||
text = text.Substring(13);
|
||||
unsafe
|
||||
{
|
||||
fixed (char* e = text)
|
||||
e[0] = char.ToUpper(e[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
var xmlDoc = DebugCommands.GetXml(member.Type);
|
||||
if (xmlDoc != null)
|
||||
text = xmlDoc;
|
||||
}
|
||||
|
||||
_memberCache.Add(member, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
// [Reference: MSDN Magazine, October 2019, Volume 34 Number 10, "Accessing XML Documentation via Reflection"]
|
||||
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/october/csharp-accessing-xml-documentation-via-reflection
|
||||
|
||||
private string GetXmlKey(MethodInfo methodInfo)
|
||||
{
|
||||
var typeGenericMap = new Dictionary<string, int>();
|
||||
var methodGenericMap = new Dictionary<string, int>();
|
||||
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
||||
|
||||
if (methodInfo.DeclaringType.IsGenericType)
|
||||
{
|
||||
var methods = methodInfo.DeclaringType.GetGenericTypeDefinition().GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
methodInfo = methods.First(x => x.MetadataToken == methodInfo.MetadataToken);
|
||||
}
|
||||
|
||||
Type[] typeGenericArguments = methodInfo.DeclaringType.GetGenericArguments();
|
||||
for (int i = 0; i < typeGenericArguments.Length; i++)
|
||||
{
|
||||
Type typeGeneric = typeGenericArguments[i];
|
||||
typeGenericMap[typeGeneric.Name] = i;
|
||||
}
|
||||
|
||||
Type[] methodGenericArguments = methodInfo.GetGenericArguments();
|
||||
for (int i = 0; i < methodGenericArguments.Length; i++)
|
||||
{
|
||||
Type methodGeneric = methodGenericArguments[i];
|
||||
methodGenericMap[methodGeneric.Name] = i;
|
||||
}
|
||||
|
||||
string declarationTypeString = GetXmlKey(methodInfo.DeclaringType, false, typeGenericMap, methodGenericMap);
|
||||
string memberNameString = methodInfo.Name;
|
||||
string methodGenericArgumentsString = methodGenericMap.Count > 0 ? "``" + methodGenericMap.Count : string.Empty;
|
||||
string parametersString = parameterInfos.Length > 0 ? "(" + string.Join(",", methodInfo.GetParameters().Select(x => GetXmlKey(x.ParameterType, true, typeGenericMap, methodGenericMap))) + ")" : string.Empty;
|
||||
|
||||
string key = "M:" + declarationTypeString + "." + memberNameString + methodGenericArgumentsString + parametersString;
|
||||
if (methodInfo.Name is "op_Implicit" || methodInfo.Name is "op_Explicit")
|
||||
{
|
||||
key += "~" + GetXmlKey(methodInfo.ReturnType, true, typeGenericMap, methodGenericMap);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private string GetXmlKey(ConstructorInfo constructorInfo)
|
||||
{
|
||||
var typeGenericMap = new Dictionary<string, int>();
|
||||
var methodGenericMap = new Dictionary<string, int>();
|
||||
ParameterInfo[] parameterInfos = constructorInfo.GetParameters();
|
||||
|
||||
Type[] typeGenericArguments = constructorInfo.DeclaringType.GetGenericArguments();
|
||||
for (int i = 0; i < typeGenericArguments.Length; i++)
|
||||
{
|
||||
Type typeGeneric = typeGenericArguments[i];
|
||||
typeGenericMap[typeGeneric.Name] = i;
|
||||
}
|
||||
|
||||
string declarationTypeString = GetXmlKey(constructorInfo.DeclaringType, false, typeGenericMap, methodGenericMap);
|
||||
string methodGenericArgumentsString = methodGenericMap.Count > 0 ? "``" + methodGenericMap.Count : string.Empty;
|
||||
string parametersString = parameterInfos.Length > 0 ? "(" + string.Join(",", constructorInfo.GetParameters().Select(x => GetXmlKey(x.ParameterType, true, typeGenericMap, methodGenericMap))) + ")" : string.Empty;
|
||||
|
||||
return "M:" + declarationTypeString + "." + "#ctor" + methodGenericArgumentsString + parametersString;
|
||||
}
|
||||
|
||||
internal static string GetXmlKey(Type type, bool isMethodParameter, Dictionary<string, int> typeGenericMap, Dictionary<string, int> methodGenericMap)
|
||||
{
|
||||
if (type.IsGenericParameter)
|
||||
{
|
||||
if (methodGenericMap.TryGetValue(type.Name, out var methodIndex))
|
||||
return "``" + methodIndex;
|
||||
if (typeGenericMap.TryGetValue(type.Name, out var typeKey))
|
||||
return "`" + typeKey;
|
||||
return "`";
|
||||
}
|
||||
if (type.HasElementType)
|
||||
{
|
||||
string elementTypeString = GetXmlKey(type.GetElementType(), isMethodParameter, typeGenericMap, methodGenericMap);
|
||||
if (type.IsPointer)
|
||||
return elementTypeString + "*";
|
||||
if (type.IsByRef)
|
||||
return elementTypeString + "@";
|
||||
if (type.IsArray)
|
||||
{
|
||||
int rank = type.GetArrayRank();
|
||||
string arrayDimensionsString = rank > 1 ? "[" + string.Join(",", Enumerable.Repeat("0:", rank)) + "]" : "[]";
|
||||
return elementTypeString + arrayDimensionsString;
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
string prefaceString = type.IsNested ? GetXmlKey(type.DeclaringType, isMethodParameter, typeGenericMap, methodGenericMap) + "." : type.Namespace + ".";
|
||||
string typeNameString = isMethodParameter ? Regex.Replace(type.Name, @"`\d+", string.Empty) : type.Name;
|
||||
string genericArgumentsString = type.IsGenericType && isMethodParameter ? "{" + string.Join(",", type.GetGenericArguments().Select(argument => GetXmlKey(argument, true, typeGenericMap, methodGenericMap))) + "}" : string.Empty;
|
||||
return prefaceString + typeNameString + genericArgumentsString;
|
||||
}
|
||||
|
||||
private static string GetXmlKey(string typeFullNameString)
|
||||
{
|
||||
return Regex.Replace(typeFullNameString, @"\[.*\]", string.Empty).Replace('+', '.');
|
||||
}
|
||||
|
||||
private static string FilterWhitespaces(string str)
|
||||
{
|
||||
if (str.Contains(" ", StringComparison.Ordinal))
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var prev = str[0];
|
||||
sb.Append(prev);
|
||||
for (int i = 1; i < str.Length; i++)
|
||||
{
|
||||
var c = str[i];
|
||||
if (prev != ' ' || c != ' ')
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
prev = c;
|
||||
}
|
||||
str = sb.ToString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetXmlDocs(Assembly assembly)
|
||||
{
|
||||
if (!_xmlCache.TryGetValue(assembly, out var result))
|
||||
{
|
||||
Profiler.BeginEvent("GetXmlDocs");
|
||||
|
||||
var assemblyPath = Utils.GetAssemblyLocation(assembly);
|
||||
var assemblyName = assembly.GetName().Name;
|
||||
var xmlFilePath = Path.ChangeExtension(assemblyPath, ".xml");
|
||||
if (!File.Exists(assemblyPath) && !string.IsNullOrEmpty(assemblyPath))
|
||||
{
|
||||
var uri = new UriBuilder(assemblyPath);
|
||||
var path = Uri.UnescapeDataString(uri.Path);
|
||||
xmlFilePath = Path.Combine(Path.GetDirectoryName(path), assemblyName + ".xml");
|
||||
}
|
||||
if (File.Exists(xmlFilePath))
|
||||
{
|
||||
Profiler.BeginEvent(assemblyName);
|
||||
try
|
||||
{
|
||||
// Parse xml documentation
|
||||
using (var xmlReader = XmlReader.Create(new StreamReader(xmlFilePath)))
|
||||
{
|
||||
result = new Dictionary<string, string>();
|
||||
while (xmlReader.Read())
|
||||
{
|
||||
if (xmlReader.NodeType == XmlNodeType.Element && string.Equals(xmlReader.Name, "member", StringComparison.Ordinal))
|
||||
{
|
||||
string rawName = xmlReader["name"];
|
||||
var memberReader = xmlReader.ReadSubtree();
|
||||
if (memberReader.ReadToDescendant("summary"))
|
||||
{
|
||||
// Remove <see cref=""/> and replace them with the captured group (the content of the cref). Additionally, getting rid of prefixes
|
||||
const string crefPattern = @"<see\s+cref=""(?:[A-Z]:FlaxEngine\.)?([^""]+)""\s*\/>";
|
||||
result[rawName] = Regex.Replace(memberReader.ReadInnerXml(), crefPattern, "$1").Replace('\n', ' ').Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors
|
||||
}
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
_xmlCache[assembly] = result;
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void OnTypesCleared()
|
||||
{
|
||||
_typeCache.Clear();
|
||||
_memberCache.Clear();
|
||||
_xmlCache.Clear();
|
||||
DebugCommands.ClearXml();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -347,6 +347,14 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Viewport"), EditorOrder(1550)]
|
||||
public InputBinding Down = new InputBinding(KeyboardKeys.Q);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "C")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1551)]
|
||||
public InputBinding ZoomIn = new InputBinding(KeyboardKeys.C);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Z")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1552)]
|
||||
public InputBinding ZoomOut = new InputBinding(KeyboardKeys.Z);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "None")]
|
||||
[EditorDisplay("Viewport", "Toggle Camera Rotation"), EditorOrder(1560)]
|
||||
public InputBinding CameraToggleRotation = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
@@ -187,7 +187,7 @@ namespace FlaxEditor.Options
|
||||
public float DirectionGizmoScale { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the opacity of the main viewports <see cref="Gizmo.DirectionGizmo"/> background.
|
||||
/// Gets or sets a value for the opacity of the main viewports <see cref="Gizmo.DirectionGizmo"/> background. Background will only show when the gizmo is hovered.
|
||||
/// </summary>
|
||||
[DefaultValue(0.1f), Limit(0.0f, 1.0f)]
|
||||
[EditorDisplay("Direction Gizmo"), EditorOrder(502), Tooltip("The background opacity of the of the direction gizmo in the main viewport.")]
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
#elif PLATFORM_MAC
|
||||
#include "Engine/Platform/Apple/AppleUtils.h"
|
||||
#include <AppKit/AppKit.h>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
@@ -68,10 +71,14 @@ namespace
|
||||
if (!launcherPath.HasChars() || !FileSystem::FileExists(exePath))
|
||||
return;
|
||||
|
||||
if (launchOverridePath != String::Empty)
|
||||
installations->Add(New<RiderInstallation>(launchOverridePath, versionMember->value.GetText()));
|
||||
else
|
||||
installations->Add(New<RiderInstallation>(exePath, versionMember->value.GetText()));
|
||||
String installPath = launchOverridePath != String::Empty ? launchOverridePath : exePath;
|
||||
StringUtils::PathRemoveRelativeParts(installPath);
|
||||
for (RiderInstallation* installation : *installations)
|
||||
{
|
||||
if (installation->path == installPath)
|
||||
return;
|
||||
}
|
||||
installations->Add(New<RiderInstallation>(installPath, versionMember->value.GetText()));
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
@@ -221,17 +228,29 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
String applicationSupportFolder;
|
||||
FileSystem::GetSpecialFolderPath(SpecialFolder::ProgramData, applicationSupportFolder);
|
||||
|
||||
NSURL* appURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:@"com.jetbrains.rider"];
|
||||
if (appURL != nullptr)
|
||||
{
|
||||
const String appPath = AppleUtils::ToString((CFStringRef)[appURL path]);
|
||||
SearchDirectory(&installations, appPath / TEXT("Contents/Resources"), appPath);
|
||||
}
|
||||
|
||||
Array<String> subMacDirectories;
|
||||
FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-0/"));
|
||||
FileSystem::GetChildDirectories(subMacDirectories, applicationSupportFolder / TEXT("JetBrains/Toolbox/apps/Rider/ch-1/"));
|
||||
for (const String& directory : subMacDirectories)
|
||||
{
|
||||
String riderAppDirectory = directory / TEXT("Rider.app/Contents/Resources");
|
||||
SearchDirectory(&installations, riderAppDirectory);
|
||||
String riderAppPath = directory / TEXT("Rider.app");
|
||||
SearchDirectory(&installations, riderAppPath / TEXT("Contents/Resources"), riderAppPath);
|
||||
}
|
||||
|
||||
// Check the local installer version
|
||||
SearchDirectory(&installations, TEXT("/Applications/Rider.app/Contents/Resources"));
|
||||
SearchDirectory(&installations, TEXT("/Applications/Rider.app/Contents/Resources"), TEXT("/Applications/Rider.app"));
|
||||
|
||||
String userFolder;
|
||||
FileSystem::GetSpecialFolderPath(SpecialFolder::Documents, userFolder);
|
||||
String riderAppPath = userFolder / TEXT("../Applications/Rider.app");
|
||||
SearchDirectory(&installations, riderAppPath / TEXT("Contents/Resources"), riderAppPath);
|
||||
#endif
|
||||
|
||||
for (const String& directory : subDirectories)
|
||||
|
||||
@@ -807,8 +807,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "A", true, null, 0),
|
||||
NodeElementArchetype.Factory.Input(1, "B", true, null, 1),
|
||||
NodeElementArchetype.Factory.Input(0, "UV", true, null, 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Center", true, null, 1),
|
||||
NodeElementArchetype.Factory.Input(2, "Radius", true, typeof(float), 2, 0),
|
||||
NodeElementArchetype.Factory.Input(3, "Hardness", true, typeof(float), 3, 1),
|
||||
NodeElementArchetype.Factory.Input(4, "Invert", true, typeof(bool), 4, 2),
|
||||
|
||||
@@ -8,6 +8,7 @@ using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.Tabs;
|
||||
using FlaxEditor.Modules;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEditor.Viewport.Modes;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -307,7 +308,7 @@ namespace FlaxEditor.Tools
|
||||
public VertexPaintingGizmo Gizmo;
|
||||
|
||||
public VertexColorsPreviewMode PreviewMode = VertexColorsPreviewMode.RGB;
|
||||
public float PreviewVertexSize = 6.0f;
|
||||
public float PreviewVertexSize = 4.0f;
|
||||
public float BrushSize = 100.0f;
|
||||
public float BrushStrength = 1.0f;
|
||||
public float BrushFalloff = 1.0f;
|
||||
@@ -402,7 +403,7 @@ namespace FlaxEditor.Tools
|
||||
if (meshDatas == null)
|
||||
throw new Exception("Missing mesh data of the model to paint.");
|
||||
var instanceTransform = _selectedModel.Transform;
|
||||
var brushSphere = new BoundingSphere(_hitLocation, _gizmoMode.BrushSize);
|
||||
var brushSphere = new BoundingSphere(_hitLocation, _gizmoMode.BrushSize * 0.5f);
|
||||
if (_paintUpdateCount == 0 && !_selectedModel.HasVertexColors)
|
||||
{
|
||||
// Initialize the instance vertex colors with originals from the asset
|
||||
@@ -509,6 +510,13 @@ namespace FlaxEditor.Tools
|
||||
return;
|
||||
}
|
||||
|
||||
// Increase or decrease brush size with scroll
|
||||
if (Input.GetKey(KeyboardKeys.Shift) && !Input.GetMouseButton(MouseButton.Right))
|
||||
{
|
||||
_gizmoMode.BrushSize += dt * _gizmoMode.BrushSize * Input.Mouse.ScrollDelta * 5f;
|
||||
_gizmoMode.BrushSize = Mathf.Clamp(_gizmoMode.BrushSize, 0.0001f, 100000.0f);
|
||||
}
|
||||
|
||||
// Perform detailed tracing to find cursor location for the brush
|
||||
var ray = Owner.MouseRay;
|
||||
var view = new Ray(Owner.ViewPosition, Owner.ViewDirection);
|
||||
@@ -570,7 +578,7 @@ namespace FlaxEditor.Tools
|
||||
}
|
||||
if (_brushModel && _brushMaterial)
|
||||
{
|
||||
_brushMaterial.SetParameterValue("Color", new Color(1.0f, 0.85f, 0.0f)); // TODO: expose to editor options
|
||||
_brushMaterial.SetParameterValue("Color", new Color(1.0f, 0.85f, 0.0f));
|
||||
_brushMaterial.SetParameterValue("DepthBuffer", Owner.RenderTask.Buffers.DepthBuffer);
|
||||
Quaternion rotation = RootNode.RaycastNormalRotation(ref _hitNormal);
|
||||
Matrix transform = Matrix.Scaling(_gizmoMode.BrushSize * 0.01f) * Matrix.RotationQuaternion(rotation) * Matrix.Translation(_hitLocation - viewOrigin);
|
||||
@@ -586,8 +594,10 @@ namespace FlaxEditor.Tools
|
||||
_verticesPreviewMaterial = FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.WiresDebugMaterial);
|
||||
}
|
||||
var instanceTransform = _selectedModel.Transform;
|
||||
var modelScaleMatrix = Matrix.Scaling(_gizmoMode.PreviewVertexSize * 0.01f);
|
||||
var brushSphere = new BoundingSphere(_hitLocation, _gizmoMode.BrushSize);
|
||||
var distanceScale = (float)Vector3.Distance(instanceTransform.Translation, renderContext.View.Position) / (10.0f * Units.Meters2Units);
|
||||
var vertexScale = Mathf.Lerp(0.005f, 0.01f, Mathf.Saturate(distanceScale));
|
||||
var modelScaleMatrix = Matrix.Scaling(_gizmoMode.PreviewVertexSize * vertexScale);
|
||||
var brushSphere = new BoundingSphere(_hitLocation, _gizmoMode.BrushSize * 0.5f);
|
||||
var lodIndex = _gizmoMode.ModelLOD == -1 ? RenderTools.ComputeModelLOD(_selectedModel.Model, ref renderContext.View.Position, (float)_selectedModel.Sphere.Radius, ref renderContext) : _gizmoMode.ModelLOD;
|
||||
lodIndex = Mathf.Clamp(lodIndex, 0, meshDatas.Length - 1);
|
||||
var lodData = meshDatas[lodIndex];
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
private Transform _startMove;
|
||||
private Transform _endMove;
|
||||
private float _moveStartTime = -1;
|
||||
private float _additionalFOV;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this viewport is animating movement.
|
||||
@@ -32,6 +33,15 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
/// </summary>
|
||||
public Vector3 TargetPoint = new Vector3(-200);
|
||||
|
||||
/// <summary>
|
||||
/// Additional field of view used for zooming the camera in and out.
|
||||
/// </summary>
|
||||
public float AdditionalZoomFOV
|
||||
{
|
||||
get => _additionalFOV;
|
||||
private set => _additionalFOV = Mathf.Clamp(value, 5 - Viewport.FieldOfView, 160f - Viewport.FieldOfView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets view.
|
||||
/// </summary>
|
||||
@@ -216,7 +226,7 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
pitch += mouseDelta.Y;
|
||||
}
|
||||
|
||||
// Zoom in/out
|
||||
// Zoom in/out with mouse wheel
|
||||
if (input.IsZooming && !input.IsRotating)
|
||||
{
|
||||
position += forward * (Viewport.MouseWheelZoomSpeedFactor * input.MouseWheelDelta * 25.0f);
|
||||
@@ -226,6 +236,17 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
}
|
||||
}
|
||||
|
||||
// Zoom in and out by changing FOV
|
||||
if (input.IsRotating && (input.ZoomInDown || input.ZoomOutDown))
|
||||
{
|
||||
float delta = (input.ZoomInDown ? -0.8f : 0.8f);
|
||||
AdditionalZoomFOV += delta;
|
||||
}
|
||||
else if (!input.IsRotating)
|
||||
{
|
||||
AdditionalZoomFOV = 0f;
|
||||
}
|
||||
|
||||
// Move camera with the gizmo
|
||||
if (input.IsOrbiting && isUsingGizmo)
|
||||
{
|
||||
|
||||
@@ -51,6 +51,16 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
public bool IsOrbiting;
|
||||
|
||||
/// <summary>
|
||||
/// The zoom in state.
|
||||
/// </summary>
|
||||
public bool ZoomInDown;
|
||||
|
||||
/// <summary>
|
||||
/// The zoom out state.
|
||||
/// </summary>
|
||||
public bool ZoomOutDown;
|
||||
|
||||
/// <summary>
|
||||
/// The is control down flag.
|
||||
/// </summary>
|
||||
@@ -109,6 +119,10 @@ namespace FlaxEditor.Viewport
|
||||
IsAltDown = window.GetKey(KeyboardKeys.Alt);
|
||||
WasAltDownBefore = prevInput.WasAltDownBefore || prevInput.IsAltDown;
|
||||
|
||||
InputOptions inputOptions = Editor.Instance.Options.Options.Input;
|
||||
ZoomInDown = window.GetKey(inputOptions.ZoomIn.Key);
|
||||
ZoomOutDown = window.GetKey(inputOptions.ZoomOut.Key);
|
||||
|
||||
IsMouseRightDown = useMouse && window.GetMouseButton(MouseButton.Right);
|
||||
IsMouseMiddleDown = useMouse && window.GetMouseButton(MouseButton.Middle);
|
||||
IsMouseLeftDown = useMouse && window.GetMouseButton(MouseButton.Left);
|
||||
@@ -1433,7 +1447,10 @@ namespace FlaxEditor.Viewport
|
||||
else
|
||||
{
|
||||
float aspect = Width / Height;
|
||||
Matrix.PerspectiveFov(_fieldOfView * Mathf.DegreesToRadians, aspect, _nearPlane, _farPlane, out result);
|
||||
float fov = _fieldOfView;
|
||||
if (_camera is FPSCamera fpsCam)
|
||||
fov += fpsCam.AdditionalZoomFOV;
|
||||
Matrix.PerspectiveFov(fov * Mathf.DegreesToRadians, aspect, _nearPlane, _farPlane, out result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -706,7 +706,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
base.OnLeftMouseButtonDown();
|
||||
|
||||
if (!IsAltKeyDown)
|
||||
if (!IsAltKeyDown && !_directionGizmo.IsMouseOver)
|
||||
_rubberBandSelector.TryStartingRubberBandSelection(_viewMousePos);
|
||||
}
|
||||
|
||||
@@ -714,7 +714,7 @@ namespace FlaxEditor.Viewport
|
||||
protected override void OnLeftMouseButtonUp()
|
||||
{
|
||||
// Skip if was controlling mouse or mouse is not over the area
|
||||
if (_prevInput.IsControllingMouse || !Bounds.Contains(ref _viewMousePos))
|
||||
if (_prevInput.IsControllingMouse || !Bounds.Contains(ref _viewMousePos) || _directionGizmo.IsMouseOver)
|
||||
return;
|
||||
|
||||
// Select rubberbanded rect actor nodes or pick with gizmo
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
@@ -14,7 +15,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
public class AnimatedModelPreview : AssetPreview
|
||||
{
|
||||
private AnimatedModel _previewModel;
|
||||
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
|
||||
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton, _nodeNameSizeButton;
|
||||
private bool _showNodes, _showBounds, _showFloor, _showNodesNames;
|
||||
private StaticModel _floorModel;
|
||||
private bool _playAnimation, _playAnimationOnce, _autoAdjustCamera = true;
|
||||
@@ -110,9 +111,16 @@ namespace FlaxEditor.Viewport.Previews
|
||||
ShowDebugDraw = true;
|
||||
if (_showNodesNamesButton != null)
|
||||
_showNodesNamesButton.Checked = value;
|
||||
if (_nodeNameSizeButton != null)
|
||||
_nodeNameSizeButton.Enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The font size used in the node name debug draw.
|
||||
/// </summary>
|
||||
public int NodeNamesSize = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show animated model bounding box debug view.
|
||||
/// </summary>
|
||||
@@ -208,6 +216,16 @@ namespace FlaxEditor.Viewport.Previews
|
||||
_showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor);
|
||||
_showFloorButton.IndexInParent = 1;
|
||||
_showFloorButton.CloseMenuOnClick = false;
|
||||
|
||||
// Skeleton Names Size
|
||||
_nodeNameSizeButton = ViewWidgetButtonMenu.AddButton("Skeleton Names Size");
|
||||
_nodeNameSizeButton.CloseMenuOnClick = false;
|
||||
var nodeNameSizeValue = new IntValueBox(NodeNamesSize, 118, 2, 70.0f, 1, 32)
|
||||
{
|
||||
Parent = _nodeNameSizeButton
|
||||
};
|
||||
_nodeNameSizeButton.Enabled = ShowNodesNames;
|
||||
nodeNameSizeValue.ValueChanged += () => NodeNamesSize = nodeNameSizeValue.Value;
|
||||
}
|
||||
|
||||
// Enable shadows
|
||||
@@ -378,7 +396,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
if (nodesMask != null && !nodesMask[nodeIndex])
|
||||
continue;
|
||||
//var t = new Transform(pose[nodeIndex].TranslationVector, Quaternion.Identity, new Float3(0.1f));
|
||||
DebugDraw.DrawText(nodes[nodeIndex].Name, pose[nodeIndex].TranslationVector, Color.White, 20, 0.0f, 0.1f);
|
||||
DebugDraw.DrawText(nodes[nodeIndex].Name, pose[nodeIndex].TranslationVector, Color.White, NodeNamesSize, 0.0f, 0.25f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
@@ -14,6 +13,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <seealso cref="AnimatedModelPreview" />
|
||||
public class AnimationPreview : AnimatedModelPreview
|
||||
{
|
||||
private bool _baseModelMissing;
|
||||
private ViewportWidgetButton _playPauseButton;
|
||||
|
||||
/// <summary>
|
||||
@@ -94,14 +94,23 @@ namespace FlaxEditor.Viewport.Previews
|
||||
|
||||
var style = Style.Current;
|
||||
var skinnedModel = SkinnedModel;
|
||||
var baseModelMissing = false;
|
||||
if (skinnedModel == null)
|
||||
{
|
||||
Render2D.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords);
|
||||
baseModelMissing = true;
|
||||
}
|
||||
else if (!skinnedModel.IsLoaded)
|
||||
{
|
||||
Render2D.DrawText(style.FontLarge, skinnedModel.LastLoadFailed ? "Failed to load" : "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
||||
baseModelMissing = true;
|
||||
}
|
||||
if (_baseModelMissing && !baseModelMissing)
|
||||
{
|
||||
// Focus model when base model appears
|
||||
ResetCamera();
|
||||
}
|
||||
_baseModelMissing = baseModelMissing;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -132,7 +132,8 @@ namespace FlaxEditor.Windows
|
||||
|
||||
if (item is AssetItem assetItem)
|
||||
{
|
||||
if (assetItem.IsLoaded)
|
||||
var asset = FlaxEngine.Content.GetAsset(assetItem.ID);
|
||||
if (asset != null && (asset.IsLoaded || asset.LastLoadFailed))
|
||||
cm.AddButton("Reload", assetItem.Reload);
|
||||
cm.AddButton("Copy asset ID", () => Clipboard.Text = JsonSerializer.GetStringID(assetItem.ID));
|
||||
cm.AddButton("Select actors using this asset", () => Editor.SceneEditing.SelectActorsUsingAsset(assetItem.ID));
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace FlaxEditor.Windows
|
||||
private readonly ScaledRenderOutputControl _viewport;
|
||||
private readonly GameRoot _guiRoot;
|
||||
private bool _showGUI = true, _editGUI = true;
|
||||
private bool _showDebugDraw = false;
|
||||
private bool _showDebugDraw = true;
|
||||
private bool _audioMuted = false;
|
||||
private float _audioVolume = 1;
|
||||
private bool _isMaximized = false, _isUnlockingMouse = false;
|
||||
@@ -821,7 +821,8 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
if (Camera.MainCamera == null)
|
||||
var mainRenderTask = MainRenderTask.Instance;
|
||||
if (Camera.MainCamera == null && (mainRenderTask == null || !mainRenderTask.IsCustomRendering))
|
||||
{
|
||||
var style = Style.Current;
|
||||
Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
||||
|
||||
@@ -98,10 +98,10 @@ namespace FlaxEditor.Windows
|
||||
[NoSerialize, DefaultValue(1.0f), Limit(0.05f, 5, 0)]
|
||||
[EditorOrder(1400), EditorDisplay("Quality")]
|
||||
[Tooltip("The scale of the rendering resolution relative to the output dimensions. If lower than 1 the scene and postprocessing will be rendered at a lower resolution and upscaled to the output backbuffer.")]
|
||||
public float RenderingPercentage
|
||||
public float RenderScale
|
||||
{
|
||||
get => MainRenderTask.Instance.RenderingPercentage;
|
||||
set => MainRenderTask.Instance.RenderingPercentage = value;
|
||||
get => MainRenderTask.Instance.RenderScale;
|
||||
set => MainRenderTask.Instance.RenderScale = value;
|
||||
}
|
||||
|
||||
[NoSerialize, DefaultValue(RenderingUpscaleLocation.AfterAntiAliasingPass), VisibleIf(nameof(UpscaleLocation_Visible))]
|
||||
@@ -113,7 +113,7 @@ namespace FlaxEditor.Windows
|
||||
set => MainRenderTask.Instance.UpscaleLocation = value;
|
||||
}
|
||||
|
||||
private bool UpscaleLocation_Visible => MainRenderTask.Instance.RenderingPercentage < 1.0f;
|
||||
private bool UpscaleLocation_Visible => MainRenderTask.Instance.RenderScale < 1.0f;
|
||||
|
||||
[NoSerialize, DefaultValue(1.0f), Limit(0, 1)]
|
||||
[EditorOrder(1500), EditorDisplay("Quality"), Tooltip("The global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices.")]
|
||||
|
||||
@@ -176,7 +176,8 @@ namespace FlaxEditor.Windows
|
||||
if (Owner != null && (!Owner._searchPopup?.Visible ?? true))
|
||||
{
|
||||
// Focus back the input field as user want to modify command from history
|
||||
Owner._searchPopup?.Hide();
|
||||
Owner.HideHistory();
|
||||
Owner.HideSearch();
|
||||
Owner.RootWindow.Focus();
|
||||
Owner.Focus();
|
||||
Owner.OnKeyDown(key);
|
||||
@@ -209,6 +210,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private OutputLogWindow _window;
|
||||
private ItemsListContextMenu _searchPopup;
|
||||
private ItemsListContextMenu _historyPopup;
|
||||
private bool _isSettingText;
|
||||
|
||||
public CommandLineBox(float x, float y, float width, OutputLogWindow window)
|
||||
@@ -226,6 +228,24 @@ namespace FlaxEditor.Windows
|
||||
_isSettingText = false;
|
||||
}
|
||||
|
||||
private void HideSearch()
|
||||
{
|
||||
if (_searchPopup != null)
|
||||
{
|
||||
_searchPopup.Hide();
|
||||
_searchPopup = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void HideHistory()
|
||||
{
|
||||
if (_historyPopup != null)
|
||||
{
|
||||
_historyPopup.Dispose();
|
||||
_historyPopup = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowPopup(ref ItemsListContextMenu cm, IEnumerable<string> commands, string searchText = null)
|
||||
{
|
||||
if (cm == null)
|
||||
@@ -295,7 +315,7 @@ namespace FlaxEditor.Windows
|
||||
private void OnRootWindowLostFocus()
|
||||
{
|
||||
// Prevent popup from staying active when editor window looses focus
|
||||
_searchPopup?.Hide();
|
||||
HideSearch();
|
||||
if (RootWindow?.Window != null)
|
||||
RootWindow.Window.LostFocus -= OnRootWindowLostFocus;
|
||||
}
|
||||
@@ -330,6 +350,7 @@ namespace FlaxEditor.Windows
|
||||
if (isWhitespaceOnly)
|
||||
DebugCommands.GetAllCommands(out commands);
|
||||
|
||||
HideHistory();
|
||||
ShowPopup(ref _searchPopup, isWhitespaceOnly ? commands : matches, text);
|
||||
|
||||
if (isWhitespaceOnly)
|
||||
@@ -342,7 +363,7 @@ namespace FlaxEditor.Windows
|
||||
return;
|
||||
}
|
||||
}
|
||||
_searchPopup?.Hide();
|
||||
HideSearch();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -353,7 +374,8 @@ namespace FlaxEditor.Windows
|
||||
case KeyboardKeys.Return:
|
||||
{
|
||||
// Run command
|
||||
_searchPopup?.Hide();
|
||||
HideSearch();
|
||||
HideHistory();
|
||||
var command = Text.Trim();
|
||||
if (command.Length == 0)
|
||||
return true;
|
||||
@@ -430,9 +452,8 @@ namespace FlaxEditor.Windows
|
||||
if (_window._commandHistory != null && _window._commandHistory.Count != 0)
|
||||
{
|
||||
// Show command history popup
|
||||
_searchPopup?.Hide();
|
||||
ItemsListContextMenu cm = null;
|
||||
ShowPopup(ref cm, _window._commandHistory);
|
||||
HideSearch();
|
||||
ShowPopup(ref _historyPopup, _window._commandHistory);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -57,6 +57,15 @@ public:
|
||||
{
|
||||
return &_knowledge;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blackboard of a given type.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
FORCE_INLINE T* GetBlackboard()
|
||||
{
|
||||
return _knowledge.GetBlackboard<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last behavior tree execution result.
|
||||
|
||||
@@ -205,10 +205,9 @@ bool BehaviorKnowledge::Set(const StringAnsiView& path, const Variant& value)
|
||||
|
||||
bool BehaviorKnowledge::HasGoal(ScriptingTypeHandle type) const
|
||||
{
|
||||
for (int32 i = 0; i < Goals.Count(); i++)
|
||||
for (const Variant& goal : Goals)
|
||||
{
|
||||
const ScriptingTypeHandle goalType = Scripting::FindScriptingType(Goals[i].Type.GetTypeName());
|
||||
if (goalType == type)
|
||||
if (goal.Type.GetScriptingType() == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -218,8 +217,7 @@ const Variant& BehaviorKnowledge::GetGoal(ScriptingTypeHandle type) const
|
||||
{
|
||||
for (const Variant& goal : Goals)
|
||||
{
|
||||
const ScriptingTypeHandle goalType = Scripting::FindScriptingType(goal.Type.GetTypeName());
|
||||
if (goalType == type)
|
||||
if (goal.Type.GetScriptingType() == type)
|
||||
return goal;
|
||||
}
|
||||
return Variant::Null;
|
||||
@@ -242,10 +240,9 @@ void BehaviorKnowledge::RemoveGoal(ScriptingTypeHandle type)
|
||||
{
|
||||
for (int32 i = 0; i < Goals.Count(); i++)
|
||||
{
|
||||
const ScriptingTypeHandle goalType = Scripting::FindScriptingType(Goals[i].Type.GetTypeName());
|
||||
if (goalType == type)
|
||||
if (Goals[i].Type.GetScriptingType() == type)
|
||||
{
|
||||
Goals.RemoveAt(i);
|
||||
Goals.RemoveAtKeepOrder(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,18 @@ public:
|
||||
RemoveGoal(T::TypeInitializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blackboard of a given type.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
FORCE_INLINE T* GetBlackboard()
|
||||
{
|
||||
auto* structure = Blackboard.AsStructure<T>();
|
||||
if (structure)
|
||||
return structure;
|
||||
return Cast<T>((ScriptingObject*)Blackboard);
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Compares two values and returns the comparision result.
|
||||
|
||||
@@ -11,6 +11,18 @@ using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
partial class Behavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the blackboard of the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> The blackboard type.</typeparam>
|
||||
public T GetBlackboard<T>()
|
||||
{
|
||||
return Knowledge.GetBlackboard<T>();
|
||||
}
|
||||
}
|
||||
|
||||
partial class BehaviorKnowledge
|
||||
{
|
||||
/// <summary>
|
||||
@@ -33,6 +45,16 @@ namespace FlaxEngine
|
||||
{
|
||||
RemoveGoal(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blackboard of the given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> The blackboard type.</typeparam>
|
||||
[Unmanaged]
|
||||
public T GetBlackboard<T>()
|
||||
{
|
||||
return (T)Blackboard;
|
||||
}
|
||||
}
|
||||
|
||||
partial class BehaviorTreeRootNode
|
||||
|
||||
@@ -61,14 +61,14 @@ void AnimGraphBase::Clear()
|
||||
StateTransitions.Resize(0);
|
||||
|
||||
// Base
|
||||
GraphType::Clear();
|
||||
VisjectGraph::Clear();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void AnimGraphBase::GetReferences(Array<Guid>& output) const
|
||||
{
|
||||
GraphType::GetReferences(output);
|
||||
VisjectGraph::GetReferences(output);
|
||||
|
||||
// Collect references from nested graph (assets used in state machines)
|
||||
for (const auto* subGraph : SubGraphs)
|
||||
|
||||
@@ -873,65 +873,41 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu
|
||||
switch (param->Type.Type)
|
||||
{
|
||||
case VariantType::Float2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsFloat2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Float3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsFloat3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Float4:
|
||||
case VariantType::Color:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsFloat4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsDouble2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsDouble3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double4:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsDouble4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Int2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsInt2().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Int3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsInt3().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Int4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsInt4().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Matrix:
|
||||
{
|
||||
|
||||
@@ -272,6 +272,7 @@ String Asset::ToString() const
|
||||
|
||||
void Asset::OnDeleteObject()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Asset.Unload");
|
||||
ASSERT(IsInMainThread());
|
||||
|
||||
// Send event to the gameplay so it can release handle to this asset
|
||||
|
||||
@@ -163,7 +163,7 @@ VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box)
|
||||
|
||||
// Add to the calling stack
|
||||
VisualScripting::StackFrame frame = *stack.Stack;
|
||||
frame.Node = parentNode;
|
||||
frame.Node = (VisualScriptGraphNode*)parentNode;
|
||||
frame.Box = box;
|
||||
frame.PreviousFrame = stack.Stack;
|
||||
stack.Stack = &frame;
|
||||
@@ -189,7 +189,7 @@ VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box)
|
||||
VisjectExecutor::Graph* VisualScriptExecutor::GetCurrentGraph() const
|
||||
{
|
||||
auto& stack = ThreadStacks.Get();
|
||||
return stack.Stack && stack.Stack->Script ? &stack.Stack->Script->Graph : nullptr;
|
||||
return stack.Stack && stack.Stack->Script ? (Graph*)&stack.Stack->Script->Graph : nullptr;
|
||||
}
|
||||
|
||||
void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value)
|
||||
@@ -339,7 +339,7 @@ void VisualScriptExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
obj = Value::Null;
|
||||
#else
|
||||
const ScriptingTypeHandle type = Scripting::FindScriptingType(StringAnsiView(typeNameAnsi.Get(), typeName.Length()));
|
||||
const ScriptingTypeHandle objType = Scripting::FindScriptingType(obj.Type.GetTypeName());
|
||||
const ScriptingTypeHandle objType = obj.Type.GetScriptingType();
|
||||
if (!type || !objType || !objType.IsSubclassOf(type))
|
||||
obj = Value::Null;
|
||||
#endif
|
||||
@@ -432,7 +432,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
// Call Impulse or Pure Method
|
||||
if (boxBase->ID == 0 || (bool)node->Values[3])
|
||||
{
|
||||
auto& cache = node->Data.InvokeMethod;
|
||||
auto& cache = ((VisualScriptGraphNode*)node)->Data.InvokeMethod;
|
||||
if (!cache.Method)
|
||||
{
|
||||
// Load method signature
|
||||
@@ -667,7 +667,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
// Get Field
|
||||
case 7:
|
||||
{
|
||||
auto& cache = node->Data.GetSetField;
|
||||
auto& cache = ((VisualScriptGraphNode*)node)->Data.GetSetField;
|
||||
if (!cache.Field)
|
||||
{
|
||||
const auto typeName = (StringView)node->Values[0];
|
||||
@@ -753,7 +753,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
// Get Field
|
||||
case 8:
|
||||
{
|
||||
auto& cache = node->Data.GetSetField;
|
||||
auto& cache = ((VisualScriptGraphNode*)node)->Data.GetSetField;
|
||||
if (!cache.Field)
|
||||
{
|
||||
const auto typeName = (StringView)node->Values[0];
|
||||
|
||||
@@ -12,11 +12,47 @@
|
||||
#define VISUAL_SCRIPT_GRAPH_MAX_CALL_STACK 250
|
||||
#define VISUAL_SCRIPT_DEBUGGING USE_EDITOR
|
||||
|
||||
#define VisualScriptGraphNode VisjectGraphNode<>
|
||||
|
||||
class VisualScripting;
|
||||
class VisualScriptingBinaryModule;
|
||||
|
||||
/// <summary>
|
||||
/// Visual Script graph node.
|
||||
/// </summary>
|
||||
class VisualScriptGraphNode : public VisjectGraphNode<>
|
||||
{
|
||||
public:
|
||||
struct InvokeMethodData
|
||||
{
|
||||
void* Method;
|
||||
BinaryModule* Module;
|
||||
int32 ParamsCount;
|
||||
uint32 OutParamsMask;
|
||||
bool IsStatic;
|
||||
};
|
||||
|
||||
struct GetSetFieldData
|
||||
{
|
||||
void* Field;
|
||||
BinaryModule* Module;
|
||||
bool IsStatic;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Custom cached data per node type. Compact to use as small amount of memory as possible.
|
||||
/// </summary>
|
||||
struct AdditionalData
|
||||
{
|
||||
union
|
||||
{
|
||||
InvokeMethodData InvokeMethod;
|
||||
GetSetFieldData GetSetField;
|
||||
};
|
||||
};
|
||||
|
||||
// The custom per-node data. Used to cache data for faster usage at runtime.
|
||||
AdditionalData Data;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The Visual Script graph data.
|
||||
/// </summary>
|
||||
|
||||
@@ -564,10 +564,7 @@ ContentLoadTask* BinaryAsset::createLoadingTask()
|
||||
loadTask = preLoadChunksTask;
|
||||
}
|
||||
|
||||
// Before asset loading we have to initialize storage
|
||||
// TODO: maybe in build game we could do it in place?
|
||||
// This step is only for opening asset files in background and upgrading them
|
||||
// In build game we have only a few packages which are ready to use
|
||||
// Before asset loading we have to initialize storage and pull the asset header
|
||||
auto initTask = New<InitAssetTask>(this);
|
||||
initTask->ContinueWith(loadTask);
|
||||
loadTask = initTask;
|
||||
|
||||
@@ -113,7 +113,7 @@ void AssetsCache::Init()
|
||||
}
|
||||
|
||||
// Use only valid entries
|
||||
if (IsEntryValid(e))
|
||||
if (IsEntryValid(e) != EntryValidation::Invalid)
|
||||
_registry.Add(e.Info.ID, e);
|
||||
else
|
||||
rejectedCount++;
|
||||
@@ -295,14 +295,23 @@ bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
|
||||
auto& e = i->Value;
|
||||
if (e.Info.Path == path)
|
||||
{
|
||||
if (!IsEntryValid(e))
|
||||
const auto validation = IsEntryValid(e);
|
||||
if (validation == EntryValidation::Invalid)
|
||||
{
|
||||
LOG(Warning, "Missing file from registry: \'{0}\':{1}:{2}", e.Info.Path, e.Info.ID, e.Info.TypeName);
|
||||
_registry.Remove(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
if (validation == EntryValidation::Inaccessible && !e.WarnedInaccessible)
|
||||
{
|
||||
e.WarnedInaccessible = true;
|
||||
LOG(Warning, "Asset file locked, keeping cached entry: \'{0}\':{1}:{2}", e.Info.Path, e.Info.ID, e.Info.TypeName);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Found valid or inaccessible but return cached info either way
|
||||
result = true;
|
||||
info = e.Info;
|
||||
}
|
||||
@@ -322,13 +331,22 @@ bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
|
||||
auto e = _registry.TryGet(id);
|
||||
if (e != nullptr)
|
||||
{
|
||||
if (!IsEntryValid(*e))
|
||||
const auto validation = IsEntryValid(*e);
|
||||
if (validation == EntryValidation::Invalid)
|
||||
{
|
||||
LOG(Warning, "Missing file from registry: \'{0}\':{1}:{2}", e->Info.Path, e->Info.ID, e->Info.TypeName);
|
||||
_registry.Remove(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
if (validation == EntryValidation::Inaccessible && !e->WarnedInaccessible)
|
||||
{
|
||||
e->WarnedInaccessible = true;
|
||||
LOG(Warning, "Asset file locked, keeping cached entry: \'{0}\':{1}:{2}", e->Info.Path, e->Info.ID, e->Info.TypeName);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Found
|
||||
result = true;
|
||||
info = e->Info;
|
||||
@@ -360,13 +378,13 @@ void AssetsCache::GetAllByTypeName(const StringView& typeName, Array<Guid>& resu
|
||||
void AssetsCache::RegisterAssets(FlaxStorage* storage)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
ASSERT(storage);
|
||||
|
||||
// Get all entries
|
||||
Array<FlaxStorage::Entry> entries;
|
||||
storage->GetEntries(entries);
|
||||
ASSERT(entries.HasItems());
|
||||
if (entries.IsEmpty())
|
||||
return;
|
||||
|
||||
ASSETS_CACHE_LOCK();
|
||||
auto storagePath = storage->GetPath();
|
||||
@@ -567,60 +585,70 @@ bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPa
|
||||
|
||||
#endif
|
||||
|
||||
bool AssetsCache::IsEntryValid(Entry& e)
|
||||
AssetsCache::EntryValidation AssetsCache::IsEntryValid(Entry& e)
|
||||
{
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
// Check if file exists
|
||||
if (FileSystem::FileExists(e.Info.Path))
|
||||
if (!FileSystem::FileExists(e.Info.Path))
|
||||
return EntryValidation::Invalid;
|
||||
|
||||
// Check if file hasn't been modified
|
||||
const auto fileModified = FileSystem::GetFileLastEditTime(e.Info.Path);
|
||||
if (fileModified == e.FileModified)
|
||||
{
|
||||
// Check if file hasn't been modified
|
||||
const auto fileModified = FileSystem::GetFileLastEditTime(e.Info.Path);
|
||||
if (fileModified == e.FileModified)
|
||||
return true;
|
||||
|
||||
const auto extension = FileSystem::GetExtension(e.Info.Path).ToLower();
|
||||
|
||||
// Check if it's a binary asset
|
||||
if (ContentStorageManager::IsFlaxStorageExtension(extension))
|
||||
{
|
||||
// Validate ID within storage container
|
||||
const auto storage = ContentStorageManager::GetStorage(e.Info.Path);
|
||||
if (storage)
|
||||
{
|
||||
// Check if storage at given location contains that asset
|
||||
const bool isValid = storage->HasAsset(e.Info);
|
||||
|
||||
// Update entry and mark cache as dirty
|
||||
e.FileModified = fileModified;
|
||||
_isDirty = true;
|
||||
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
// Check for json resource
|
||||
else if (JsonStorageProxy::IsValidExtension(extension))
|
||||
{
|
||||
// Check Json storage layer
|
||||
Guid jsonId;
|
||||
String jsonTypeName;
|
||||
if (JsonStorageProxy::GetAssetInfo(e.Info.Path, jsonId, jsonTypeName))
|
||||
{
|
||||
const bool isValid = e.Info.ID == jsonId && e.Info.TypeName == jsonTypeName;
|
||||
|
||||
// Update entry and mark cache as dirty
|
||||
e.FileModified = fileModified;
|
||||
_isDirty = true;
|
||||
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
e.WarnedInaccessible = false;
|
||||
return EntryValidation::Valid;
|
||||
}
|
||||
|
||||
return false;
|
||||
const auto extension = FileSystem::GetExtension(e.Info.Path).ToLower();
|
||||
|
||||
// Check if it's a binary asset
|
||||
if (ContentStorageManager::IsFlaxStorageExtension(extension))
|
||||
{
|
||||
// Validate ID within storage container
|
||||
const auto storage = ContentStorageManager::GetStorage(e.Info.Path);
|
||||
if (storage)
|
||||
{
|
||||
// Check if storage at given location contains that asset
|
||||
const bool isValid = storage->HasAsset(e.Info);
|
||||
|
||||
// Update entry and mark cache as dirty
|
||||
e.FileModified = fileModified;
|
||||
e.WarnedInaccessible = false;
|
||||
_isDirty = true;
|
||||
|
||||
return isValid ? EntryValidation::Valid : EntryValidation::Invalid;
|
||||
}
|
||||
}
|
||||
// Check for json resource
|
||||
else if (JsonStorageProxy::IsValidExtension(extension))
|
||||
{
|
||||
// Check Json storage layer
|
||||
Guid jsonId;
|
||||
String jsonTypeName;
|
||||
if (JsonStorageProxy::GetAssetInfo(e.Info.Path, jsonId, jsonTypeName))
|
||||
{
|
||||
const bool isValid = e.Info.ID == jsonId && e.Info.TypeName == jsonTypeName;
|
||||
|
||||
// Update entry and mark cache as dirty
|
||||
e.FileModified = fileModified;
|
||||
e.WarnedInaccessible = false;
|
||||
_isDirty = true;
|
||||
|
||||
return isValid ? EntryValidation::Valid : EntryValidation::Invalid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown file type
|
||||
return EntryValidation::Invalid;
|
||||
}
|
||||
|
||||
// File exists but cannot be read (likely locked by git or another process)
|
||||
return EntryValidation::Inaccessible;
|
||||
#else
|
||||
// In game we don't care about it because all cached asset entries are valid (precached)
|
||||
// Skip only entries with missing file
|
||||
return e.Info.Path.HasChars();
|
||||
return e.Info.Path.HasChars() ? EntryValidation::Valid : EntryValidation::Invalid;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -58,6 +58,11 @@ public:
|
||||
/// The file modified date.
|
||||
/// </summary>
|
||||
DateTime FileModified;
|
||||
|
||||
/// <summary>
|
||||
/// True if a warning about this entry being inaccessible has already been logged (prevents log spam). Runtime-only, not serialized.
|
||||
/// </summary>
|
||||
bool WarnedInaccessible = false;
|
||||
#endif
|
||||
|
||||
Entry()
|
||||
@@ -73,6 +78,27 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Result of validating an asset cache entry.
|
||||
/// </summary>
|
||||
enum class EntryValidation
|
||||
{
|
||||
/// <summary>
|
||||
/// File verified, contains this asset.
|
||||
/// </summary>
|
||||
Valid,
|
||||
|
||||
/// <summary>
|
||||
/// File missing or contains a different asset.
|
||||
/// </summary>
|
||||
Invalid,
|
||||
|
||||
/// <summary>
|
||||
/// File exists but cannot be opened (locked by another process).
|
||||
/// </summary>
|
||||
Inaccessible,
|
||||
};
|
||||
|
||||
typedef Dictionary<Guid, Entry> Registry;
|
||||
typedef Dictionary<String, Guid> PathsMapping;
|
||||
|
||||
@@ -232,6 +258,6 @@ public:
|
||||
/// Determines whether cached asset entry is valid.
|
||||
/// </summary>
|
||||
/// <param name="e">The asset entry.</param>
|
||||
/// <returns>True if is valid, otherwise false.</returns>
|
||||
bool IsEntryValid(Entry& e);
|
||||
/// <returns>The validation result.</returns>
|
||||
EntryValidation IsEntryValid(Entry& e);
|
||||
};
|
||||
|
||||
@@ -54,6 +54,7 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
|
||||
Locker.Lock();
|
||||
|
||||
// Try fast lookup
|
||||
bool wasCached = true;
|
||||
FlaxStorage* storage;
|
||||
if (!StorageMap.TryGet(path, storage))
|
||||
{
|
||||
@@ -74,6 +75,7 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
|
||||
|
||||
// Register storage container
|
||||
StorageMap.Add(path, storage);
|
||||
wasCached = false;
|
||||
}
|
||||
|
||||
// Build reference (before releasing the lock so ContentStorageSystem::Job won't delete it when running from async thread)
|
||||
@@ -90,6 +92,8 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
|
||||
if (loadFailed)
|
||||
{
|
||||
LOG(Error, "Failed to load {0}.", path);
|
||||
if (wasCached)
|
||||
return result;
|
||||
Locker.Lock();
|
||||
StorageMap.Remove(path);
|
||||
if (storage->IsPackage())
|
||||
|
||||
@@ -243,9 +243,9 @@ FlaxStorage::~FlaxStorage()
|
||||
{
|
||||
// Validate if has been disposed
|
||||
ASSERT(IsDisposed());
|
||||
CHECK(_chunksLock == 0);
|
||||
CHECK(_refCount == 0);
|
||||
CHECK(_isUnloadingData == 0);
|
||||
CHECK_NO_RETURN(_chunksLock == 0);
|
||||
CHECK_NO_RETURN(_refCount == 0);
|
||||
CHECK_NO_RETURN(_isUnloadingData == 0);
|
||||
ASSERT(_chunks.IsEmpty());
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
// Float
|
||||
|
||||
static_assert(sizeof(Float2) == 8, "Invalid Float2 type size.");
|
||||
static_assert(sizeof(Int2) == 8, "Invalid Int2 type size.");
|
||||
static_assert(sizeof(Double2) == 16, "Invalid Double2 type size.");
|
||||
|
||||
template<>
|
||||
const Float2 Float2::Zero(0.0f);
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
// Float
|
||||
|
||||
static_assert(sizeof(Float3) == 12, "Invalid Float3 type size.");
|
||||
static_assert(sizeof(Int3) == 12, "Invalid Int3 type size.");
|
||||
static_assert(sizeof(Double3) == 24, "Invalid Double3 type size.");
|
||||
|
||||
template<>
|
||||
const Float3 Float3::Zero(0.0f);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
// Float
|
||||
|
||||
static_assert(sizeof(Float4) == 16, "Invalid Float4 type size.");
|
||||
static_assert(sizeof(Int4) == 16, "Invalid Int4 type size.");
|
||||
static_assert(sizeof(Double4) == 32, "Invalid Double4 type size.");
|
||||
|
||||
template<>
|
||||
const Float4 Float4::Zero(0.0f);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Engine/Core/Collections/HashFunctions.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Mathd.h"
|
||||
#include "Engine/Core/Math/BoundingBox.h"
|
||||
@@ -124,7 +125,7 @@ VariantType::VariantType(Types type, const StringAnsiView& typeName, bool static
|
||||
VariantType::VariantType(Types type, const ScriptingType& sType)
|
||||
: VariantType(type)
|
||||
{
|
||||
SetTypeName(sType.Fullname, sType.Module->CanReload);
|
||||
SetTypeName(sType.Fullname, !sType.Module->CanReload);
|
||||
}
|
||||
|
||||
VariantType::VariantType(Types type, const MClass* klass)
|
||||
@@ -344,13 +345,13 @@ void VariantType::SetTypeName(const StringAnsiView& typeName, bool staticName)
|
||||
|
||||
void VariantType::SetTypeName(const ScriptingType& type)
|
||||
{
|
||||
SetTypeName(type.Fullname, type.Module->CanReload);
|
||||
SetTypeName(type.Fullname, !type.Module->CanReload);
|
||||
}
|
||||
|
||||
void VariantType::SetTypeName(const MClass& klass)
|
||||
{
|
||||
#if USE_CSHARP
|
||||
SetTypeName(klass.GetFullName(), klass.GetAssembly()->CanReload());
|
||||
SetTypeName(klass.GetFullName(), !klass.GetAssembly()->CanReload());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -361,6 +362,11 @@ const char* VariantType::GetTypeName() const
|
||||
return InBuiltTypesTypeNames[Type];
|
||||
}
|
||||
|
||||
ScriptingTypeHandle VariantType::GetScriptingType() const
|
||||
{
|
||||
return Scripting::FindScriptingType(GetTypeName());
|
||||
}
|
||||
|
||||
VariantType VariantType::GetElementType() const
|
||||
{
|
||||
if (Type == Array)
|
||||
@@ -3193,6 +3199,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3216,6 +3225,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3239,6 +3251,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3262,6 +3277,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3285,6 +3303,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3308,6 +3329,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3331,6 +3355,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3354,6 +3381,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3377,6 +3407,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3520,6 +3553,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3(v.AsBool ? 1.0 : 0.0));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4(v.AsBool ? 1.0 : 0.0));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2(v.AsBool ? 1 : 0));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3(v.AsBool ? 1 : 0));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4(v.AsBool ? 1 : 0));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, v.AsBool ? 1 : 0);
|
||||
default: ;
|
||||
@@ -3558,6 +3597,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3((double)v.AsInt16));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsInt16));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2(v.AsInt16));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3(v.AsInt16));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4(v.AsInt16));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsInt16);
|
||||
default: ;
|
||||
@@ -3588,6 +3633,18 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Float3((float)v.AsInt));
|
||||
case VariantType::Float4:
|
||||
return Variant(Float4((float)v.AsInt));
|
||||
case VariantType::Double2:
|
||||
return Variant(Double2((double)v.AsInt));
|
||||
case VariantType::Double3:
|
||||
return Variant(Double3((double)v.AsInt));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsInt));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2(v.AsInt));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3(v.AsInt));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4(v.AsInt));
|
||||
case VariantType::Color:
|
||||
return Variant(Color((float)v.AsInt));
|
||||
case VariantType::Enum:
|
||||
@@ -3628,6 +3685,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3((double)v.AsUint16));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsUint16));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2(v.AsUint16));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3(v.AsUint16));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4(v.AsUint16));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsUint16);
|
||||
default: ;
|
||||
@@ -3666,6 +3729,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3((double)v.AsUint));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsUint));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsUint));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsUint));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsUint));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsUint);
|
||||
default: ;
|
||||
@@ -3704,6 +3773,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3((double)v.AsInt64));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsInt64));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsInt64));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsInt64));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsInt64));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsInt64);
|
||||
default: ;
|
||||
@@ -3723,25 +3798,31 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Uint16:
|
||||
return Variant((uint16)v.AsUint16);
|
||||
case VariantType::Uint:
|
||||
return Variant((uint32)v.AsUint);
|
||||
return Variant((uint32)v.AsUint64);
|
||||
case VariantType::Float:
|
||||
return Variant((float)v.AsUint64);
|
||||
case VariantType::Double:
|
||||
return Variant((double)v.AsUint64);
|
||||
case VariantType::Float2:
|
||||
return Variant(Float2((float)v.AsInt));
|
||||
return Variant(Float2((float)v.AsUint64));
|
||||
case VariantType::Float3:
|
||||
return Variant(Float3((float)v.AsInt));
|
||||
return Variant(Float3((float)v.AsUint64));
|
||||
case VariantType::Float4:
|
||||
return Variant(Float4((float)v.AsInt));
|
||||
return Variant(Float4((float)v.AsUint64));
|
||||
case VariantType::Color:
|
||||
return Variant(Color((float)v.AsInt));
|
||||
return Variant(Color((float)v.AsUint64));
|
||||
case VariantType::Double2:
|
||||
return Variant(Double2((double)v.AsInt));
|
||||
return Variant(Double2((double)v.AsUint64));
|
||||
case VariantType::Double3:
|
||||
return Variant(Double3((double)v.AsInt));
|
||||
return Variant(Double3((double)v.AsUint64));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsInt));
|
||||
return Variant(Double4((double)v.AsUint64));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsUint64));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsUint64));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsUint64));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsInt);
|
||||
default: ;
|
||||
@@ -3780,6 +3861,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3(v.AsFloat));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4(v.AsFloat));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsFloat));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsFloat));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsFloat));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsFloat);
|
||||
default: ;
|
||||
@@ -3818,6 +3905,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3(v.AsDouble));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4(v.AsDouble));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsFloat));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsFloat));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsFloat));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsDouble);
|
||||
default: ;
|
||||
@@ -4246,6 +4339,7 @@ void Variant::AllocStructure()
|
||||
AsBlob.Length = 2;
|
||||
AsBlob.Data = Allocator::Allocate(AsBlob.Length);
|
||||
*((int16*)AsBlob.Data) = 0;
|
||||
MARK_CONTENT_DEPRECATED();
|
||||
}
|
||||
#if USE_CSHARP
|
||||
else if (const auto mclass = Scripting::FindClass(typeName))
|
||||
@@ -4371,6 +4465,13 @@ uint32 GetHash(const Variant& key)
|
||||
return GetHash((void*)key.AsObject);
|
||||
case VariantType::Structure:
|
||||
case VariantType::Blob:
|
||||
case VariantType::Transform:
|
||||
case VariantType::Matrix:
|
||||
#if USE_LARGE_WORLDS
|
||||
case VariantType::BoundingSphere:
|
||||
case VariantType::BoundingBox:
|
||||
case VariantType::Ray:
|
||||
#endif
|
||||
return Crc::MemCrc32(key.AsBlob.Data, key.AsBlob.Length);
|
||||
case VariantType::Asset:
|
||||
return GetHash((void*)key.AsAsset);
|
||||
@@ -4380,6 +4481,24 @@ uint32 GetHash(const Variant& key)
|
||||
return GetHash(*(Guid*)key.AsData);
|
||||
case VariantType::Typename:
|
||||
return GetHash((const char*)key.AsBlob.Data);
|
||||
case VariantType::Float2:
|
||||
return GetHash(*(const Float2*)key.AsData);
|
||||
case VariantType::Float3:
|
||||
return GetHash(*(const Float3*)key.AsData);
|
||||
case VariantType::Float4:
|
||||
return GetHash(*(const Float4*)key.AsData);
|
||||
case VariantType::Int2:
|
||||
return GetHash(*(const Int2*)key.AsData);
|
||||
case VariantType::Int3:
|
||||
return GetHash(*(const Int3*)key.AsData);
|
||||
case VariantType::Int4:
|
||||
return GetHash(*(const Int4*)key.AsData);
|
||||
case VariantType::Double2:
|
||||
return GetHash(*(const Double2*)key.AsData);
|
||||
case VariantType::Double3:
|
||||
return GetHash(*(const Double3*)key.AsData);
|
||||
case VariantType::Double4:
|
||||
return GetHash(*(const Double4*)key.AsBlob.Data);
|
||||
case VariantType::ManagedObject:
|
||||
#if USE_CSHARP
|
||||
return key.MANAGED_GC_HANDLE ? (uint32)MCore::Object::GetHashCode(MCore::GCHandle::GetTarget(key.MANAGED_GC_HANDLE)) : 0;
|
||||
|
||||
@@ -151,6 +151,7 @@ public:
|
||||
void SetTypeName(const ScriptingType& type);
|
||||
void SetTypeName(const MClass& klass);
|
||||
const char* GetTypeName() const;
|
||||
ScriptingTypeHandle GetScriptingType() const;
|
||||
VariantType GetElementType() const;
|
||||
// Drops custom type name into the name allocated by the scripting module to reduce memory allocations when referencing types.
|
||||
void Inline();
|
||||
@@ -420,6 +421,15 @@ public:
|
||||
return MoveTemp(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static typename TEnableIf<TIsEnum<T>::Value, Variant>::Type Enum(const T value)
|
||||
{
|
||||
Variant v;
|
||||
v.SetType(VariantType(VariantType::Enum, StaticType<T>().GetType()));
|
||||
v.AsUint64 = (uint64)value;
|
||||
return MoveTemp(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static typename TEnableIf<!TIsEnum<T>::Value && !TIsPointer<T>::Value, Variant>::Type Structure(VariantType&& type, const T& value)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,14 @@ public class Debug : EngineModule
|
||||
{
|
||||
options.PublicDefinitions.Add("COMPILE_WITH_DEBUG_DRAW");
|
||||
}
|
||||
|
||||
if (options.Target.IsEditor || options.Configuration != TargetConfiguration.Release)
|
||||
{
|
||||
// Used by DebugCommands to parse Xml documentation
|
||||
options.ScriptingAPI.SystemReferences.Add("System.Xml");
|
||||
options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter");
|
||||
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
#include "Engine/Scripting/ManagedCLR/MMethod.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MField.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MProperty.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
|
||||
#define WITH_HELP (USE_EDITOR || !BUILD_RELEASE) && USE_CSHARP
|
||||
|
||||
struct CommandData
|
||||
{
|
||||
String Name;
|
||||
@@ -26,6 +29,38 @@ struct CommandData
|
||||
void* MethodGet = nullptr;
|
||||
void* MethodSet = nullptr;
|
||||
void* Field = nullptr;
|
||||
#if WITH_HELP
|
||||
mutable String Help;
|
||||
|
||||
StringView GetHelp() const
|
||||
{
|
||||
if (Help.IsEmpty())
|
||||
{
|
||||
if (dynamic_cast<ManagedBinaryModule*>(Module))
|
||||
{
|
||||
// Get C# type and member name
|
||||
const MClass* mclass = nullptr;
|
||||
StringAnsiView name;
|
||||
if (auto field = (MField*)Field)
|
||||
{
|
||||
mclass = field->GetParentClass();
|
||||
name = field->GetName();
|
||||
}
|
||||
else if (auto method = (MMethod*)(Method ? Method : (MethodGet ? MethodGet : MethodSet)))
|
||||
{
|
||||
mclass = method->GetParentClass();
|
||||
name = method->GetName();
|
||||
}
|
||||
|
||||
// Use Xml docs reader used by Editor to get tooltips
|
||||
auto getXmlInternal = DebugCommands::TypeInitializer.GetClass()->GetMethod("GetXmlInternal", 2);
|
||||
void* params[2] = { INTERNAL_TYPE_GET_OBJECT(mclass->GetType()), MUtils::ToString(name) };
|
||||
Help = MUtils::ToString((MString*)getXmlInternal->Invoke(nullptr, params, nullptr));
|
||||
}
|
||||
}
|
||||
return Help;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void PrettyPrint(StringBuilder& sb, const Variant& value)
|
||||
{
|
||||
@@ -50,7 +85,7 @@ struct CommandData
|
||||
else if (value.Type.Type == VariantType::Structure)
|
||||
{
|
||||
// Prettify structure printing
|
||||
ScriptingTypeHandle resultType = Scripting::FindScriptingType(value.Type.GetTypeName());
|
||||
ScriptingTypeHandle resultType = value.Type.GetScriptingType();
|
||||
if (resultType)
|
||||
{
|
||||
Array<void*> fields;
|
||||
@@ -122,7 +157,11 @@ struct CommandData
|
||||
// Parse arguments
|
||||
if (args == StringView(TEXT("?"), 1))
|
||||
{
|
||||
LOG(Warning, "TODO: debug commands help/docs printing"); // TODO: debug commands help/docs printing (use CodeDocsModule that parses XML docs)
|
||||
#if WITH_HELP
|
||||
// Print command description
|
||||
LOG(Info, "> {} ?", Name);
|
||||
LOG_STR(Info, GetHelp());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
Array<Variant> params;
|
||||
@@ -356,6 +395,22 @@ namespace
|
||||
InitCommands();
|
||||
Locker.Unlock();
|
||||
}
|
||||
|
||||
const CommandData* GetCommand(StringView command)
|
||||
{
|
||||
if (command.FindLast(' ') != -1)
|
||||
command = command.Left(command.Find(' '));
|
||||
// TODO: fix missing string handle on 1st command execution (command gets invalid after InitCommands due to dotnet GC or dotnet interop handles flush)
|
||||
String commandCopy = command;
|
||||
command = commandCopy;
|
||||
EnsureInited();
|
||||
for (auto& e : Commands)
|
||||
{
|
||||
if (e.Name == command)
|
||||
return &e;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
class DebugCommandsService : public EngineService
|
||||
@@ -475,30 +530,32 @@ void DebugCommands::GetAllCommands(Array<StringView>& commands)
|
||||
DebugCommands::CommandFlags DebugCommands::GetCommandFlags(StringView command)
|
||||
{
|
||||
CommandFlags result = CommandFlags::None;
|
||||
if (command.FindLast(' ') != -1)
|
||||
command = command.Left(command.Find(' '));
|
||||
// TODO: fix missing string handle on 1st command execution (command gets invalid after InitCommands due to dotnet GC or dotnet interop handles flush)
|
||||
String commandCopy = command;
|
||||
command = commandCopy;
|
||||
EnsureInited();
|
||||
for (auto& e : Commands)
|
||||
if (auto cmd = GetCommand(command))
|
||||
{
|
||||
if (e.Name == command)
|
||||
{
|
||||
if (e.Method)
|
||||
result |= CommandFlags::Exec;
|
||||
else if (e.Field)
|
||||
result |= CommandFlags::ReadWrite;
|
||||
if (e.MethodGet)
|
||||
result |= CommandFlags::Read;
|
||||
if (e.MethodSet)
|
||||
result |= CommandFlags::Write;
|
||||
break;
|
||||
}
|
||||
if (cmd->Method)
|
||||
result |= CommandFlags::Exec;
|
||||
else if (cmd->Field)
|
||||
result |= CommandFlags::ReadWrite;
|
||||
if (cmd->MethodGet)
|
||||
result |= CommandFlags::Read;
|
||||
if (cmd->MethodSet)
|
||||
result |= CommandFlags::Write;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
StringView DebugCommands::GetCommandHelp(StringView command)
|
||||
{
|
||||
StringView result;
|
||||
#if WITH_HELP
|
||||
if (auto cmd = GetCommand(command))
|
||||
{
|
||||
result = cmd->GetHelp();
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DebugCommands::Iterate(const StringView& searchText, int32& index)
|
||||
{
|
||||
EnsureInited();
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if FLAX_EDITOR || !BUILD_RELEASE
|
||||
#define WITH_HELP
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
@@ -12,4 +23,331 @@ namespace FlaxEngine
|
||||
public sealed class DebugCommand : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
partial class DebugCommands
|
||||
{
|
||||
#if WITH_HELP
|
||||
private static Dictionary<Assembly, Dictionary<string, string>> _xmlCache;
|
||||
private static StringBuilder _sb;
|
||||
|
||||
internal static void ClearXml()
|
||||
{
|
||||
if (_xmlCache == null)
|
||||
return;
|
||||
foreach (var asm in _xmlCache.Keys.ToArray())
|
||||
{
|
||||
if (asm.IsCollectible)
|
||||
_xmlCache.Remove(asm);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetXmlInternal(Type type, string memberName)
|
||||
{
|
||||
// Redirect into type when no member specified
|
||||
if (string.IsNullOrEmpty(memberName))
|
||||
return GetXml(type);
|
||||
|
||||
// Redirect property function getter/setter into owning property docs
|
||||
if (memberName.StartsWith("get_") || memberName.StartsWith("set_"))
|
||||
memberName = memberName.Substring(4);
|
||||
|
||||
// Find member of that name
|
||||
var members = type.GetMember(memberName, BindingFlags.Static | BindingFlags.Public);
|
||||
if (members.Length == 0)
|
||||
return null;
|
||||
return GetXml(members[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XML docs text for the type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>The documentation help.</returns>
|
||||
public static string GetXml(Type type)
|
||||
{
|
||||
var xml = GetXmlDocs(type.Assembly);
|
||||
if (xml != null)
|
||||
{
|
||||
var key = "T:" + GetXmlKey(type.FullName);
|
||||
if (xml.TryGetValue(key, out var xmlDoc))
|
||||
return FilterWhitespaces(xmlDoc);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XML docs text for the type member.
|
||||
/// </summary>
|
||||
/// <param name="member">The type member.</param>
|
||||
/// <returns>The documentation help.</returns>
|
||||
public static string GetXml(MemberInfo member)
|
||||
{
|
||||
string text = null;
|
||||
var xml = GetXmlDocs(member.DeclaringType.Assembly);
|
||||
if (xml != null)
|
||||
{
|
||||
// [Reference: MSDN Magazine, October 2019, Volume 34 Number 10, "Accessing XML Documentation via Reflection"]
|
||||
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/october/csharp-accessing-xml-documentation-via-reflection
|
||||
var memberType = member.MemberType;
|
||||
string key = null;
|
||||
if (memberType.HasFlag(MemberTypes.Field))
|
||||
{
|
||||
var fieldInfo = (FieldInfo)member;
|
||||
key = "F:" + GetXmlKey(fieldInfo.DeclaringType.FullName) + "." + fieldInfo.Name;
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.Property))
|
||||
{
|
||||
var propertyInfo = (PropertyInfo)member;
|
||||
key = "P:" + GetXmlKey(propertyInfo.DeclaringType.FullName) + "." + propertyInfo.Name;
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.Event))
|
||||
{
|
||||
var eventInfo = (EventInfo)member;
|
||||
key = "E:" + GetXmlKey(eventInfo.DeclaringType.FullName) + "." + eventInfo.Name;
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.Constructor))
|
||||
{
|
||||
var constructorInfo = (ConstructorInfo)member;
|
||||
key = GetXmlKey(constructorInfo);
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.Method))
|
||||
{
|
||||
var methodInfo = (MethodInfo)member;
|
||||
key = GetXmlKey(methodInfo);
|
||||
}
|
||||
else if (memberType.HasFlag(MemberTypes.TypeInfo) || memberType.HasFlag(MemberTypes.NestedType))
|
||||
{
|
||||
var typeInfo = (TypeInfo)member;
|
||||
key = "T:" + GetXmlKey(typeInfo.FullName);
|
||||
}
|
||||
if (key != null)
|
||||
{
|
||||
xml.TryGetValue(key, out text);
|
||||
text = FilterWhitespaces(text);
|
||||
}
|
||||
|
||||
// Customize tooltips for properties to be more human-readable in UI
|
||||
if (text != null && memberType.HasFlag(MemberTypes.Property) && text.StartsWith("Gets or sets ", StringComparison.Ordinal))
|
||||
{
|
||||
text = text.Substring(13);
|
||||
unsafe
|
||||
{
|
||||
fixed (char* e = text)
|
||||
e[0] = char.ToUpper(e[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static string FilterWhitespaces(string str)
|
||||
{
|
||||
if (str != null && str.Contains(" ", StringComparison.Ordinal))
|
||||
{
|
||||
if (_sb == null)
|
||||
_sb = new StringBuilder();
|
||||
else
|
||||
_sb.Clear();
|
||||
var sb = _sb;
|
||||
var prev = str[0];
|
||||
sb.Append(prev);
|
||||
for (int i = 1; i < str.Length; i++)
|
||||
{
|
||||
var c = str[i];
|
||||
if (prev != ' ' || c != ' ')
|
||||
sb.Append(c);
|
||||
prev = c;
|
||||
}
|
||||
str = sb.ToString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// [Reference: MSDN Magazine, October 2019, Volume 34 Number 10, "Accessing XML Documentation via Reflection"]
|
||||
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/october/csharp-accessing-xml-documentation-via-reflection
|
||||
|
||||
private static string GetXmlKey(MethodInfo methodInfo)
|
||||
{
|
||||
var typeGenericMap = new Dictionary<string, int>();
|
||||
var methodGenericMap = new Dictionary<string, int>();
|
||||
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
||||
|
||||
if (methodInfo.DeclaringType.IsGenericType)
|
||||
{
|
||||
var methods = methodInfo.DeclaringType.GetGenericTypeDefinition().GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
methodInfo = methods.First(x => x.MetadataToken == methodInfo.MetadataToken);
|
||||
}
|
||||
|
||||
Type[] typeGenericArguments = methodInfo.DeclaringType.GetGenericArguments();
|
||||
for (int i = 0; i < typeGenericArguments.Length; i++)
|
||||
{
|
||||
Type typeGeneric = typeGenericArguments[i];
|
||||
typeGenericMap[typeGeneric.Name] = i;
|
||||
}
|
||||
|
||||
Type[] methodGenericArguments = methodInfo.GetGenericArguments();
|
||||
for (int i = 0; i < methodGenericArguments.Length; i++)
|
||||
{
|
||||
Type methodGeneric = methodGenericArguments[i];
|
||||
methodGenericMap[methodGeneric.Name] = i;
|
||||
}
|
||||
|
||||
string declarationTypeString = GetXmlKey(methodInfo.DeclaringType, false, typeGenericMap, methodGenericMap);
|
||||
string memberNameString = methodInfo.Name;
|
||||
string methodGenericArgumentsString = methodGenericMap.Count > 0 ? "``" + methodGenericMap.Count : string.Empty;
|
||||
string parametersString = parameterInfos.Length > 0 ? "(" + string.Join(",", methodInfo.GetParameters().Select(x => GetXmlKey(x.ParameterType, true, typeGenericMap, methodGenericMap))) + ")" : string.Empty;
|
||||
|
||||
string key = "M:" + declarationTypeString + "." + memberNameString + methodGenericArgumentsString + parametersString;
|
||||
if (methodInfo.Name is "op_Implicit" || methodInfo.Name is "op_Explicit")
|
||||
{
|
||||
key += "~" + GetXmlKey(methodInfo.ReturnType, true, typeGenericMap, methodGenericMap);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private static string GetXmlKey(ConstructorInfo constructorInfo)
|
||||
{
|
||||
var typeGenericMap = new Dictionary<string, int>();
|
||||
var methodGenericMap = new Dictionary<string, int>();
|
||||
ParameterInfo[] parameterInfos = constructorInfo.GetParameters();
|
||||
|
||||
Type[] typeGenericArguments = constructorInfo.DeclaringType.GetGenericArguments();
|
||||
for (int i = 0; i < typeGenericArguments.Length; i++)
|
||||
{
|
||||
Type typeGeneric = typeGenericArguments[i];
|
||||
typeGenericMap[typeGeneric.Name] = i;
|
||||
}
|
||||
|
||||
string declarationTypeString = GetXmlKey(constructorInfo.DeclaringType, false, typeGenericMap, methodGenericMap);
|
||||
string methodGenericArgumentsString = methodGenericMap.Count > 0 ? "``" + methodGenericMap.Count : string.Empty;
|
||||
string parametersString = parameterInfos.Length > 0 ? "(" + string.Join(",", constructorInfo.GetParameters().Select(x => GetXmlKey(x.ParameterType, true, typeGenericMap, methodGenericMap))) + ")" : string.Empty;
|
||||
|
||||
return "M:" + declarationTypeString + "." + "#ctor" + methodGenericArgumentsString + parametersString;
|
||||
}
|
||||
|
||||
internal static string GetXmlKey(Type type, bool isMethodParameter, Dictionary<string, int> typeGenericMap, Dictionary<string, int> methodGenericMap)
|
||||
{
|
||||
if (type.IsGenericParameter)
|
||||
{
|
||||
if (methodGenericMap.TryGetValue(type.Name, out var methodIndex))
|
||||
return "``" + methodIndex;
|
||||
if (typeGenericMap.TryGetValue(type.Name, out var typeKey))
|
||||
return "`" + typeKey;
|
||||
return "`";
|
||||
}
|
||||
if (type.HasElementType)
|
||||
{
|
||||
string elementTypeString = GetXmlKey(type.GetElementType(), isMethodParameter, typeGenericMap, methodGenericMap);
|
||||
if (type.IsPointer)
|
||||
return elementTypeString + "*";
|
||||
if (type.IsByRef)
|
||||
return elementTypeString + "@";
|
||||
if (type.IsArray)
|
||||
{
|
||||
int rank = type.GetArrayRank();
|
||||
string arrayDimensionsString = rank > 1 ? "[" + string.Join(",", Enumerable.Repeat("0:", rank)) + "]" : "[]";
|
||||
return elementTypeString + arrayDimensionsString;
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
string prefaceString = type.IsNested ? GetXmlKey(type.DeclaringType, isMethodParameter, typeGenericMap, methodGenericMap) + "." : type.Namespace + ".";
|
||||
string typeNameString = isMethodParameter ? Regex.Replace(type.Name, @"`\d+", string.Empty) : type.Name;
|
||||
string genericArgumentsString = type.IsGenericType && isMethodParameter ? "{" + string.Join(",", type.GetGenericArguments().Select(argument => GetXmlKey(argument, true, typeGenericMap, methodGenericMap))) + "}" : string.Empty;
|
||||
return prefaceString + typeNameString + genericArgumentsString;
|
||||
}
|
||||
|
||||
private static string GetXmlKey(string typeFullNameString)
|
||||
{
|
||||
return Regex.Replace(typeFullNameString, @"\[.*\]", string.Empty).Replace('+', '.');
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetXmlDocs(Assembly assembly)
|
||||
{
|
||||
if (_xmlCache == null)
|
||||
_xmlCache = new Dictionary<Assembly, Dictionary<string, string>>();
|
||||
if (!_xmlCache.TryGetValue(assembly, out var result))
|
||||
{
|
||||
Profiler.BeginEvent("GetXmlDocs");
|
||||
|
||||
// Find XML file path (based on assembly location)
|
||||
var assemblyPath = Utils.GetAssemblyLocation(assembly);
|
||||
var assemblyName = assembly.GetName().Name;
|
||||
var xmlFilePath = Path.ChangeExtension(assemblyPath, ".xml");
|
||||
if (!File.Exists(assemblyPath) && !string.IsNullOrEmpty(assemblyPath))
|
||||
{
|
||||
var uri = new UriBuilder(assemblyPath);
|
||||
var path = Uri.UnescapeDataString(uri.Path);
|
||||
xmlFilePath = Path.Combine(Path.GetDirectoryName(path), assemblyName + ".xml");
|
||||
}
|
||||
if (File.Exists(xmlFilePath))
|
||||
{
|
||||
Profiler.BeginEvent(assemblyName);
|
||||
try
|
||||
{
|
||||
// Parse xml documentation
|
||||
using (var xmlReader = XmlReader.Create(new StreamReader(xmlFilePath)))
|
||||
{
|
||||
result = new Dictionary<string, string>();
|
||||
StringBuilder content = new StringBuilder(2048);
|
||||
while (xmlReader.Read())
|
||||
{
|
||||
if (xmlReader.NodeType == XmlNodeType.Element && string.Equals(xmlReader.Name, "member", StringComparison.Ordinal))
|
||||
{
|
||||
string rawName = xmlReader["name"];
|
||||
var memberReader = xmlReader.ReadSubtree();
|
||||
if (memberReader.ReadToDescendant("summary"))
|
||||
{
|
||||
content.Clear();
|
||||
do
|
||||
{
|
||||
if (memberReader.NodeType == XmlNodeType.Element && memberReader.Read())
|
||||
{
|
||||
while (memberReader.NodeType == XmlNodeType.Text)
|
||||
{
|
||||
content.Append(memberReader.Value);
|
||||
if (memberReader.Read() && memberReader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
var nodeRef = TrimRef(memberReader.GetAttribute("cref")); // <see cref=""/>
|
||||
if (nodeRef == null)
|
||||
nodeRef = memberReader.GetAttribute("name"); // <paramref name=""/>
|
||||
content.Append(nodeRef);
|
||||
memberReader.Read();
|
||||
|
||||
string TrimRef(string str)
|
||||
{
|
||||
if (str == null)
|
||||
return null;
|
||||
if (str.IndexOf(":FlaxEngine.") == 1)
|
||||
return str.Substring("T:FlaxEngine.".Length);
|
||||
return str.Substring("T:".Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (memberReader.NodeType == XmlNodeType.EndElement && memberReader.Name == "summary")
|
||||
break;
|
||||
} while (memberReader.Read());
|
||||
|
||||
result[rawName] = content.ToString().Trim(' ', '\r', '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors
|
||||
}
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
_xmlCache[assembly] = result;
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,17 @@ public:
|
||||
/// Returns flags of the command.
|
||||
/// </summary>
|
||||
/// <param name="command">The full name of the command.</param>
|
||||
/// <returns>Command flags.</returns>
|
||||
API_FUNCTION() static CommandFlags GetCommandFlags(StringView command);
|
||||
|
||||
/// <summary>
|
||||
/// Returns help text of the command (from documentation comment).
|
||||
/// </summary>
|
||||
/// <remarks>Only available in non-Release builds and Editor.</remarks>
|
||||
/// <param name="command">The full name of the command.</param>
|
||||
/// <returns>Command help text or empty if failed to get it.</returns>
|
||||
API_FUNCTION() static StringView GetCommandHelp(StringView command);
|
||||
|
||||
public:
|
||||
static bool Iterate(const StringView& searchText, int32& index);
|
||||
static StringView GetCommandName(int32 index);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace FlaxEngine;
|
||||
|
||||
partial class GameplayGlobals
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value of a given type from the global variables. (it must be added first).
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable to retrieve.</param>
|
||||
/// <typeparam name="T">The type of the variable to retrieve.</typeparam>
|
||||
/// <returns>The value of the variable, or default if not found or type mismatch.</returns>
|
||||
public T GetValue<T>(string name)
|
||||
{
|
||||
var obj = GetValue(name);
|
||||
if (obj is T t)
|
||||
return t;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,6 @@ API_CLASS(Static, Attributes="DebugCommand") class FLAXENGINE_API Globals
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Globals);
|
||||
|
||||
public:
|
||||
// Paths
|
||||
|
||||
// Main engine directory path.
|
||||
API_FIELD(ReadOnly) static String StartupFolder;
|
||||
|
||||
@@ -54,8 +52,6 @@ public:
|
||||
#endif
|
||||
|
||||
public:
|
||||
// State
|
||||
|
||||
// True if fatal error occurred (engine is exiting).
|
||||
// [Deprecated in v1.10]
|
||||
DEPRECATED("Use Engine::FatalError instead.") static bool FatalErrorOccurred;
|
||||
@@ -91,14 +87,10 @@ public:
|
||||
DEPRECATED("Use Engine::ExitCode instead.") static int32 ExitCode;
|
||||
|
||||
public:
|
||||
// Threading
|
||||
|
||||
// Main Engine thread id.
|
||||
API_FIELD(ReadOnly) static uint64 MainThreadID;
|
||||
|
||||
public:
|
||||
// Config
|
||||
|
||||
/// <summary>
|
||||
/// The full engine version.
|
||||
/// </summary>
|
||||
|
||||
@@ -76,9 +76,10 @@ void GPUTasksContext::OnFrameBegin()
|
||||
{
|
||||
auto task = _tasksSyncing[i];
|
||||
auto state = task->GetState();
|
||||
if (EnumHasAllFlags(task->Flags, ObjectFlags::WasMarkedToDelete))
|
||||
state = TaskState::Finished;
|
||||
if (task->GetSyncPoint() <= _currentSyncPoint && state != TaskState::Finished)
|
||||
{
|
||||
// TODO: add stats counter and count performed jobs, print to log on exit.
|
||||
task->Sync();
|
||||
}
|
||||
if (state == TaskState::Failed || state == TaskState::Canceled)
|
||||
|
||||
@@ -19,32 +19,32 @@ public:
|
||||
API_FIELD() static bool UseVSync;
|
||||
|
||||
/// <summary>
|
||||
/// Anti Aliasing quality setting.
|
||||
/// Anti Aliasing quality setting. Available values are: Low, Medium, High, Ultra (or 0, 1, 2, 3).
|
||||
/// </summary>
|
||||
API_FIELD() static Quality AAQuality;
|
||||
|
||||
/// <summary>
|
||||
/// Screen Space Reflections quality setting.
|
||||
/// Screen Space Reflections quality setting. Available values are: Low, Medium, High, Ultra (or 0, 1, 2, 3).
|
||||
/// </summary>
|
||||
API_FIELD() static Quality SSRQuality;
|
||||
|
||||
/// <summary>
|
||||
/// Screen Space Ambient Occlusion quality setting.
|
||||
/// Screen Space Ambient Occlusion quality setting. Available values are: Low, Medium, High, Ultra (or 0, 1, 2, 3).
|
||||
/// </summary>
|
||||
API_FIELD() static Quality SSAOQuality;
|
||||
|
||||
/// <summary>
|
||||
/// Volumetric Fog quality setting.
|
||||
/// Volumetric Fog quality setting. Available values are: Low, Medium, High, Ultra (or 0, 1, 2, 3).
|
||||
/// </summary>
|
||||
API_FIELD() static Quality VolumetricFogQuality;
|
||||
|
||||
/// <summary>
|
||||
/// The shadows quality.
|
||||
/// The shadows quality. Available values are: Low, Medium, High, Ultra (or 0, 1, 2, 3).
|
||||
/// </summary>
|
||||
API_FIELD() static Quality ShadowsQuality;
|
||||
|
||||
/// <summary>
|
||||
/// The shadow maps quality (textures resolution).
|
||||
/// The shadow maps quality (textures resolution). Available values are: Low, Medium, High, Ultra (or 0, 1, 2, 3).
|
||||
/// </summary>
|
||||
API_FIELD() static Quality ShadowMapsQuality;
|
||||
|
||||
@@ -59,12 +59,12 @@ public:
|
||||
API_FIELD() static bool AllowCSMBlending;
|
||||
|
||||
/// <summary>
|
||||
/// The Global SDF quality. Controls the volume texture resolution and amount of cascades to use.
|
||||
/// The Global SDF quality. Controls the volume texture resolution and amount of cascades to use. Available values are: Low, Medium, High, Ultra (or 0, 1, 2, 3).
|
||||
/// </summary>
|
||||
API_FIELD() static Quality GlobalSDFQuality;
|
||||
|
||||
/// <summary>
|
||||
/// The Global Illumination quality. Controls the quality of the GI effect.
|
||||
/// The Global Illumination quality. Controls the quality of the GI effect. Available values are: Low, Medium, High, Ultra (or 0, 1, 2, 3).
|
||||
/// </summary>
|
||||
API_FIELD() static Quality GIQuality;
|
||||
|
||||
|
||||
@@ -460,6 +460,18 @@ void MaterialParameter::Bind(BindMeta& meta) const
|
||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
|
||||
*((Float4*)(meta.Constants.Get() + _offset)) = (Float4)e->Value.AsDouble4();
|
||||
break;
|
||||
case VariantType::Int2:
|
||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Int2)));
|
||||
*((Int2*)(meta.Constants.Get() + _offset)) = (Int2)e->Value.AsInt2();
|
||||
break;
|
||||
case VariantType::Int3:
|
||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Int3)));
|
||||
*((Int3*)(meta.Constants.Get() + _offset)) = (Int3)e->Value.AsInt3();
|
||||
break;
|
||||
case VariantType::Int4:
|
||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Int4)));
|
||||
*((Int4*)(meta.Constants.Get() + _offset)) = (Int4)e->Value.AsInt4();
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,7 +621,7 @@ namespace FlaxEngine
|
||||
{
|
||||
ibData = dataPtr[IB];
|
||||
use16BitIndexBuffer = _formats[IB] == PixelFormat.R16_UInt;
|
||||
triangles = (uint)(_data[IB].Length / PixelFormatExtensions.SizeInBytes(_formats[IB]));
|
||||
triangles = (uint)(_data[IB].Length / (PixelFormatExtensions.SizeInBytes(_formats[IB]) * 3));
|
||||
}
|
||||
|
||||
if (mesh.Init(vertices, triangles, vbData, ibData, use16BitIndexBuffer, vbLayout))
|
||||
@@ -643,11 +643,16 @@ namespace FlaxEngine
|
||||
else
|
||||
{
|
||||
Float3 min = Float3.Maximum, max = Float3.Minimum;
|
||||
for (int i = 0; i < vertices; i++)
|
||||
PixelFormatSampler.Get(positionStream.Format, out var positionSampler);
|
||||
int positionStride = positionStream.Stride;
|
||||
fixed (byte* data = positionStream.Data)
|
||||
{
|
||||
Float3 pos = positionStream.GetFloat3(i);
|
||||
Float3.Min(ref min, ref pos, out min);
|
||||
Float3.Max(ref max, ref pos, out max);
|
||||
for (int i = 0; i < vertices; i++)
|
||||
{
|
||||
Float3 pos = new Float3(positionSampler.Read(data + i * positionStride));
|
||||
Float3.Min(ref min, ref pos, out min);
|
||||
Float3.Max(ref max, ref pos, out max);
|
||||
}
|
||||
}
|
||||
bounds = new BoundingBox(min, max);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,13 @@ bool ModelInstanceEntries::HasContentLoaded() const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModelInstanceEntries::ShouldSerialize(const void* otherObj) const
|
||||
{
|
||||
if (!otherObj)
|
||||
return true;
|
||||
return !(*this == *(const ModelInstanceEntries*)otherObj);
|
||||
}
|
||||
|
||||
void ModelInstanceEntries::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
SERIALIZE_GET_OTHER_OBJ(ModelInstanceEntries);
|
||||
@@ -43,12 +50,13 @@ void ModelInstanceEntries::Serialize(SerializeStream& stream, const void* otherO
|
||||
void ModelInstanceEntries::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
{
|
||||
PROFILE_MEM(Graphics);
|
||||
const DeserializeStream& entries = stream["Entries"];
|
||||
ASSERT(entries.IsArray());
|
||||
Resize(entries.Size());
|
||||
for (rapidjson::SizeType i = 0; i < entries.Size(); i++)
|
||||
const DeserializeStream& entriesData = stream[rapidjson_flax::Value(rapidjson::StringRef("Entries", 7))];
|
||||
CHECK(entriesData.IsArray());
|
||||
Resize(entriesData.Size());
|
||||
ModelInstanceEntry* entries = Get();
|
||||
for (int32 i = 0; i < Count(); i++)
|
||||
{
|
||||
At(i).Deserialize((DeserializeStream&)entries[i], modifier);
|
||||
entries[i].Deserialize((DeserializeStream&)entriesData[i], modifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ public:
|
||||
|
||||
public:
|
||||
// [ISerializable]
|
||||
bool ShouldSerialize(const void* otherObj) const override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
};
|
||||
|
||||
@@ -353,8 +353,11 @@ Viewport SceneRenderTask::GetViewport() const
|
||||
viewport = Buffers->GetViewport();
|
||||
else
|
||||
viewport = Viewport(0, 0, 1280, 720);
|
||||
viewport.Width *= RenderingPercentage;
|
||||
viewport.Height *= RenderingPercentage;
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
float renderScale = RenderingPercentage * RenderScale;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
viewport.Width *= renderScale;
|
||||
viewport.Height *= renderScale;
|
||||
return viewport;
|
||||
}
|
||||
|
||||
@@ -394,13 +397,16 @@ void SceneRenderTask::OnBegin(GPUContext* context)
|
||||
}
|
||||
|
||||
// Setup render buffers for the output rendering resolution
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
float renderScale = RenderingPercentage * RenderScale;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
if (Output)
|
||||
{
|
||||
Buffers->Init((int32)((float)Output->Width() * RenderingPercentage), (int32)((float)Output->Height() * RenderingPercentage));
|
||||
Buffers->Init((int32)((float)Output->Width() * renderScale), (int32)((float)Output->Height() * renderScale));
|
||||
}
|
||||
else if (SwapChain)
|
||||
{
|
||||
Buffers->Init((int32)((float)SwapChain->GetWidth() * RenderingPercentage), (int32)((float)SwapChain->GetHeight() * RenderingPercentage));
|
||||
Buffers->Init((int32)((float)SwapChain->GetWidth() * renderScale), (int32)((float)SwapChain->GetHeight() * renderScale));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,7 +440,10 @@ bool SceneRenderTask::Resize(int32 width, int32 height)
|
||||
PROFILE_MEM(Graphics);
|
||||
if (Output && Output->Resize(width, height))
|
||||
return true;
|
||||
if (Buffers && Buffers->Init((int32)((float)width * RenderingPercentage), (int32)((float)height * RenderingPercentage)))
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
float renderScale = RenderingPercentage * RenderScale;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
if (Buffers && Buffers->Init((int32)((float)width * renderScale), (int32)((float)height * renderScale)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -477,12 +486,6 @@ void MainRenderTask::OnBegin(GPUContext* context)
|
||||
// Use the main camera for the game (can be later overriden in Begin event by external code)
|
||||
Camera = Camera::GetMainCamera();
|
||||
|
||||
#if !USE_EDITOR
|
||||
// Sync render buffers size with the backbuffer
|
||||
const auto size = Screen::GetSize();
|
||||
Buffers->Init((int32)(size.X * RenderingPercentage), (int32)(size.Y * RenderingPercentage));
|
||||
#endif
|
||||
|
||||
SceneRenderTask::OnBegin(context);
|
||||
}
|
||||
|
||||
@@ -507,3 +510,10 @@ RenderContextBatch::RenderContextBatch(const RenderContext& context)
|
||||
Contexts.Add(context);
|
||||
EnableAsync = JobSystem::GetThreadsCount() > 1;
|
||||
}
|
||||
|
||||
void RenderContextBatch::FlushWaitLabels()
|
||||
{
|
||||
for (const int64 label : WaitLabels)
|
||||
JobSystem::Wait(label);
|
||||
WaitLabels.Clear();
|
||||
}
|
||||
|
||||
@@ -268,8 +268,14 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// The scale of the rendering resolution relative to the output dimensions. If lower than 1 the scene and postprocessing will be rendered at a lower resolution and upscaled to the output backbuffer.
|
||||
/// [Deprecated in v1.13]
|
||||
/// </summary>
|
||||
API_FIELD() float RenderingPercentage = 1.0f;
|
||||
API_FIELD() DEPRECATED("Use RenderScale instead.") float RenderingPercentage = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The scale of the rendering resolution relative to the output dimensions. If lower than 1 the scene and postprocessing will be rendered at a lower resolution and upscaled to the output backbuffer.
|
||||
/// </summary>
|
||||
API_FIELD() float RenderScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The image resolution upscale location within rendering pipeline. Unused if RenderingPercentage is 1.
|
||||
@@ -533,4 +539,9 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch
|
||||
{
|
||||
return Contexts.Get()[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for all scheduled async jobs to complete and clears WaitLabels.
|
||||
/// </summary>
|
||||
void FlushWaitLabels();
|
||||
};
|
||||
|
||||
@@ -195,7 +195,7 @@ void RenderView::CopyFrom(const Camera* camera, const Viewport* viewport)
|
||||
const Vector3 cameraPos = camera->GetPosition();
|
||||
LargeWorlds::UpdateOrigin(Origin, cameraPos);
|
||||
Position = cameraPos - Origin;
|
||||
Direction = camera->GetDirection();
|
||||
Direction = camera->GetForward();
|
||||
Near = camera->GetNearPlane();
|
||||
Far = camera->GetFarPlane();
|
||||
camera->GetMatrices(View, Projection, viewport ? *viewport : camera->GetViewport(), Origin);
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace FlaxEngine
|
||||
Vector3 cameraPos = camera.Position;
|
||||
LargeWorlds.UpdateOrigin(ref Origin, cameraPos);
|
||||
Position = cameraPos - Origin;
|
||||
Direction = camera.Direction;
|
||||
Direction = camera.Forward;
|
||||
Near = camera.NearPlane;
|
||||
Far = camera.FarPlane;
|
||||
camera.GetMatrices(out View, out Projection, ref viewport, ref Origin);
|
||||
|
||||
@@ -360,14 +360,12 @@ bool ShaderAssetBase::LoadShaderCache(ShaderCacheResult& result)
|
||||
return true;
|
||||
}
|
||||
|
||||
ASSERT(result.Data.IsValid());
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
// Read includes from cache
|
||||
IsValidShaderCache(result.Data, result.Includes);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
return result.Data.IsInvalid();
|
||||
}
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
@@ -57,7 +57,7 @@ void CmdBufferVulkan::End()
|
||||
if (vkCmdEndDebugUtilsLabelEXT)
|
||||
vkCmdEndDebugUtilsLabelEXT(GetHandle());
|
||||
#endif
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
tracy::EndVkZoneScope(_tracyZones.Last().Data);
|
||||
_tracyZones.RemoveLast();
|
||||
#endif
|
||||
@@ -101,7 +101,7 @@ void CmdBufferVulkan::BeginEvent(const Char* name, void* tracyContext)
|
||||
char buffer[60];
|
||||
int32 bufferSize = StringUtils::Copy(buffer, name, sizeof(buffer));
|
||||
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
auto& zone = _tracyZones.AddOne();
|
||||
tracy::BeginVkZoneScope(zone.Data, tracyContext, GetHandle(), buffer, bufferSize);
|
||||
#endif
|
||||
@@ -128,7 +128,7 @@ void CmdBufferVulkan::EndEvent()
|
||||
vkCmdEndDebugUtilsLabelEXT(GetHandle());
|
||||
#endif
|
||||
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
tracy::EndVkZoneScope(_tracyZones.Last().Data);
|
||||
_tracyZones.RemoveLast();
|
||||
#endif
|
||||
|
||||
@@ -43,8 +43,10 @@ private:
|
||||
FenceVulkan* _fence;
|
||||
#if GPU_ALLOW_PROFILE_EVENTS
|
||||
int32 _eventsBegin = 0;
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
struct TracyZone { byte Data[TracyVulkanZoneSize]; };
|
||||
Array<TracyZone, InlinedAllocation<32>> _tracyZones;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The latest value when command buffer was submitted.
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
#define VULKAN_USE_TIMER_QUERIES 1
|
||||
#endif
|
||||
|
||||
#ifndef VULKAN_USE_TRACY_GPU
|
||||
#define VULKAN_USE_TRACY_GPU (GPU_ENABLE_TRACY && VULKAN_USE_TIMER_QUERIES)
|
||||
#endif
|
||||
|
||||
// Fence wait operation timeout in seconds
|
||||
#ifndef VULKAN_WAIT_TIMEOUT
|
||||
#define VULKAN_WAIT_TIMEOUT 5.0f
|
||||
|
||||
@@ -111,7 +111,7 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue)
|
||||
_handlesSizes[(int32)SpirvShaderResourceBindingType::UAV] = GPU_MAX_UA_BINDED;
|
||||
#endif
|
||||
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
#if VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset && !PLATFORM_SWITCH
|
||||
// Use calibrated timestamps extension
|
||||
if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT && _device->PhysicalDeviceFeatures12.hostQueryReset)
|
||||
@@ -138,7 +138,7 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue)
|
||||
|
||||
GPUContextVulkan::~GPUContextVulkan()
|
||||
{
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
tracy::DestroyVkContext(_tracyContext);
|
||||
#endif
|
||||
for (int32 i = 0; i < _descriptorPools.Count(); i++)
|
||||
@@ -799,7 +799,7 @@ void GPUContextVulkan::FrameEnd()
|
||||
// Execute any queued layout transitions that weren't already handled by the render pass
|
||||
FlushBarriers();
|
||||
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
if (cmdBuffer)
|
||||
tracy::CollectVkContext(_tracyContext, cmdBuffer->GetHandle());
|
||||
#endif
|
||||
@@ -813,7 +813,7 @@ void GPUContextVulkan::FrameEnd()
|
||||
void GPUContextVulkan::EventBegin(const Char* name)
|
||||
{
|
||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||
#if COMPILE_WITH_PROFILER
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
void* tracyContext = _tracyContext;
|
||||
#else
|
||||
void* tracyContext = nullptr;
|
||||
|
||||
@@ -45,7 +45,7 @@ static const char* GInstanceExtensions[] =
|
||||
#if defined(VK_KHR_display) && 0
|
||||
VK_KHR_DISPLAY_EXTENSION_NAME,
|
||||
#endif
|
||||
#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
#if VULKAN_USE_TRACY_GPU && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, // Required by VK_EXT_host_query_reset (unless using Vulkan 1.1 or newer)
|
||||
#endif
|
||||
nullptr
|
||||
@@ -66,7 +66,7 @@ static const char* GDeviceExtensions[] =
|
||||
#if VK_KHR_sampler_mirror_clamp_to_edge
|
||||
VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
|
||||
#endif
|
||||
#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
#if VULKAN_USE_TRACY_GPU && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME,
|
||||
VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
|
||||
#endif
|
||||
|
||||
@@ -1677,7 +1677,7 @@ bool GPUDeviceVulkan::Init()
|
||||
VulkanPlatform::RestrictEnabledPhysicalDeviceFeatures(PhysicalDeviceFeatures, enabledFeatures);
|
||||
deviceInfo.pEnabledFeatures = &enabledFeatures;
|
||||
|
||||
#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
#if VULKAN_USE_TRACY_GPU && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
VkPhysicalDeviceHostQueryResetFeatures resetFeatures;
|
||||
if (PhysicalDeviceFeatures12.hostQueryReset)
|
||||
{
|
||||
|
||||
@@ -822,6 +822,11 @@ void Actor::SetRotation(const Matrix& value)
|
||||
}
|
||||
|
||||
void Actor::SetDirection(const Float3& value)
|
||||
{
|
||||
SetForward(value);
|
||||
}
|
||||
|
||||
void Actor::SetForward(const Float3& value)
|
||||
{
|
||||
CHECK(!value.IsNanOrInfinity());
|
||||
Quaternion orientation;
|
||||
@@ -1714,17 +1719,22 @@ Actor* Actor::Intersects(const Ray& ray, Real& distance, Vector3& normal)
|
||||
|
||||
void Actor::LookAt(const Vector3& worldPos)
|
||||
{
|
||||
const Quaternion orientation = LookingAt(worldPos);
|
||||
const Quaternion orientation = GetLookAtDirection(worldPos);
|
||||
SetOrientation(orientation);
|
||||
}
|
||||
|
||||
void Actor::LookAt(const Vector3& worldPos, const Vector3& worldUp)
|
||||
{
|
||||
const Quaternion orientation = LookingAt(worldPos, worldUp);
|
||||
const Quaternion orientation = GetLookAtDirection(worldPos, worldUp);
|
||||
SetOrientation(orientation);
|
||||
}
|
||||
|
||||
Quaternion Actor::LookingAt(const Vector3& worldPos) const
|
||||
{
|
||||
return GetLookAtDirection(worldPos);
|
||||
}
|
||||
|
||||
Quaternion Actor::GetLookAtDirection(const Vector3& worldPos) const
|
||||
{
|
||||
const Vector3 direction = worldPos - _transform.Translation;
|
||||
if (direction.LengthSquared() < ZeroTolerance)
|
||||
@@ -1752,16 +1762,20 @@ Quaternion Actor::LookingAt(const Vector3& worldPos) const
|
||||
}
|
||||
|
||||
Quaternion Actor::LookingAt(const Vector3& worldPos, const Vector3& worldUp) const
|
||||
{
|
||||
return GetLookAtDirection(worldPos, worldUp);
|
||||
}
|
||||
|
||||
Quaternion Actor::GetLookAtDirection(const Vector3& worldPos, const Vector3& worldUp) const
|
||||
{
|
||||
const Vector3 direction = worldPos - _transform.Translation;
|
||||
if (direction.LengthSquared() < ZeroTolerance)
|
||||
return _parent ? _parent->GetOrientation() : Quaternion::Identity;
|
||||
|
||||
const Float3 forward = Vector3::Normalize(direction);
|
||||
const Float3 up = Vector3::Normalize(worldUp);
|
||||
if (Math::IsOne(Float3::Dot(forward, up)))
|
||||
{
|
||||
return LookingAt(worldPos);
|
||||
}
|
||||
return GetLookAtDirection(worldPos);
|
||||
|
||||
Quaternion orientation;
|
||||
Quaternion::LookRotation(direction, up, orientation);
|
||||
|
||||
@@ -549,17 +549,35 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets actor direction vector (forward vector).
|
||||
/// [Deprecated in v1.13]
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize") FORCE_INLINE Float3 GetDirection() const
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize") DEPRECATED("Use GetForward instead.")
|
||||
FORCE_INLINE Float3 GetDirection() const
|
||||
{
|
||||
return GetForward();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor's forward vector (direction).
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize") FORCE_INLINE Float3 GetForward() const
|
||||
{
|
||||
return Float3::Transform(Float3::Forward, GetOrientation());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets actor direction vector (forward)
|
||||
/// [Deprecated in v1.13]
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set.</param>
|
||||
API_PROPERTY() void SetDirection(const Float3& value);
|
||||
API_PROPERTY() DEPRECATED("Use SetForward instead.")
|
||||
void SetDirection(const Float3& value);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the actor to align its forward vector with the passed in value (direction).
|
||||
/// </summary>
|
||||
/// <param name="value">The value to align to.</param>
|
||||
API_PROPERTY() void SetForward(const Float3& value);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -897,16 +915,31 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets rotation of the actor oriented towards the specified world position.
|
||||
/// [Deprecated in v1.13]
|
||||
/// </summary>
|
||||
/// <param name="worldPos">The world position to orient towards.</param>
|
||||
API_FUNCTION() Quaternion LookingAt(const Vector3& worldPos) const;
|
||||
API_FUNCTION() DEPRECATED("Use GetLookAtDirection instead.") Quaternion LookingAt(const Vector3& worldPos) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets rotation of the actor oriented towards the specified world position.
|
||||
/// </summary>
|
||||
/// <param name="worldPos">The world position to orient towards.</param>
|
||||
API_FUNCTION() Quaternion GetLookAtDirection(const Vector3& worldPos) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets rotation of the actor oriented towards the specified world position with upwards direction.
|
||||
/// [Deprecated in v1.13]
|
||||
/// </summary>
|
||||
/// <param name="worldPos">The world position to orient towards.</param>
|
||||
/// <param name="worldUp">The up direction that constrains up axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.</param>
|
||||
API_FUNCTION() DEPRECATED("Use GetLookAtDirection instead.") Quaternion LookingAt(const Vector3& worldPos, const Vector3& worldUp) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets rotation of the actor oriented towards the specified world position with upwards direction.
|
||||
/// </summary>
|
||||
/// <param name="worldPos">The world position to orient towards.</param>
|
||||
/// <param name="worldUp">The up direction that constrains up axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.</param>
|
||||
API_FUNCTION() Quaternion LookingAt(const Vector3& worldPos, const Vector3& worldUp) const;
|
||||
API_FUNCTION() Quaternion GetLookAtDirection(const Vector3& worldPos, const Vector3& worldUp) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -820,8 +820,7 @@ void AnimatedModel::RunBlendShapeDeformer(const MeshBase* mesh, MeshDeformationD
|
||||
|
||||
void AnimatedModel::BeginPlay(SceneBeginData* data)
|
||||
{
|
||||
if (SkinnedModel && SkinnedModel->IsLoaded())
|
||||
PreInitSkinningData();
|
||||
PreInitSkinningData();
|
||||
|
||||
// Base
|
||||
ModelInstanceActor::BeginPlay(data);
|
||||
@@ -1263,9 +1262,7 @@ void AnimatedModel::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
SERIALIZE(ShadowsMode);
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
SERIALIZE(RootMotionTarget);
|
||||
|
||||
stream.JKEY("Buffer");
|
||||
stream.Object(&Entries, other ? &other->Entries : nullptr);
|
||||
SERIALIZE_MEMBER(Buffer, Entries);
|
||||
}
|
||||
|
||||
void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
@@ -1290,8 +1287,7 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
|
||||
DESERIALIZE(ShadowsMode);
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
DESERIALIZE(RootMotionTarget);
|
||||
|
||||
Entries.DeserializeIfExists(stream, "Buffer", modifier);
|
||||
DESERIALIZE_MEMBER(Buffer, Entries);
|
||||
|
||||
// [Deprecated on 07.02.2022, expires on 07.02.2024]
|
||||
if (modifier->EngineBuild <= 6330)
|
||||
|
||||
@@ -98,99 +98,99 @@ public:
|
||||
/// <summary>
|
||||
/// The skinned model asset used for rendering.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Skeleton\")")
|
||||
AssetReference<SkinnedModel> SkinnedModel;
|
||||
|
||||
/// <summary>
|
||||
/// The animation graph asset used for the skinned mesh skeleton bones evaluation (controls the animation).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(15), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(15), DefaultValue(null), EditorDisplay(\"Skeleton\")")
|
||||
AssetReference<AnimationGraph> AnimationGraph;
|
||||
|
||||
/// <summary>
|
||||
/// If true, use per-bone motion blur on this skeletal model. It requires additional rendering, can be disabled to save performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Drawing\")")
|
||||
bool PerBoneMotionBlur = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, animation speed will be affected by the global timescale parameter.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(30), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(30), DefaultValue(true), EditorDisplay(\"Updating\")")
|
||||
bool UseTimeScale = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the animation will be updated even when an actor cannot be seen by any camera. Otherwise, the animations themselves will also stop running when the actor is off-screen.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Updating\")")
|
||||
bool UpdateWhenOffscreen = false;
|
||||
|
||||
/// <summary>
|
||||
/// The animation update delta timescale. Can be used to speed up animation playback or create slow motion effect.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Updating\")")
|
||||
float UpdateSpeed = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The animation update mode. Can be used to optimize the performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(50), DefaultValue(AnimationUpdateMode.Auto), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(50), DefaultValue(AnimationUpdateMode.Auto), EditorDisplay(\"Updating\")")
|
||||
AnimationUpdateMode UpdateMode = AnimationUpdateMode::Auto;
|
||||
|
||||
/// <summary>
|
||||
/// The master scale parameter for the actor bounding box. Helps to reduce mesh flickering effect on screen edges.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Drawing\")")
|
||||
float BoundsScale = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The custom bounds(in actor local space). If set to empty bounds then source skinned model bind pose bounds will be used.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Drawing\")")
|
||||
BoundingBox CustomBounds = BoundingBox::Zero;
|
||||
|
||||
/// <summary>
|
||||
/// The model Level Of Detail bias value. Allows to increase or decrease rendered model quality.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(80), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Skinned Model\", \"LOD Bias\")")
|
||||
API_FIELD(Attributes="EditorOrder(80), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Drawing\", \"LOD Bias\")")
|
||||
int32 LODBias = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(90), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Skinned Model\", \"Forced LOD\")")
|
||||
API_FIELD(Attributes="EditorOrder(90), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Drawing\", \"Forced LOD\")")
|
||||
int32 ForcedLOD = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The draw passes to use for rendering this object.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(100), DefaultValue(DrawPass.Default), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(100), DefaultValue(DrawPass.Default), EditorDisplay(\"Drawing\")")
|
||||
DrawPass DrawModes = DrawPass::Default;
|
||||
|
||||
/// <summary>
|
||||
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorDisplay(\"Skinned Model\"), EditorOrder(110), DefaultValue(0)")
|
||||
API_FIELD(Attributes="EditorOrder(110), DefaultValue(0), EditorDisplay(\"Drawing\")")
|
||||
int8 SortOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The shadows casting mode.
|
||||
/// [Deprecated on 26.10.2022, expires on 26.10.2024]
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Drawing\")")
|
||||
DEPRECATED() ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
|
||||
|
||||
/// <summary>
|
||||
/// The animation root motion apply target. If not specified the animated model will apply it itself.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(120), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(120), DefaultValue(null), EditorDisplay(\"Skeleton\")")
|
||||
ScriptingObjectReference<Actor> RootMotionTarget;
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// If checked, the skeleton pose will be shawn during debug shapes drawing.
|
||||
/// If checked, the skeleton pose will be shown during debug shapes drawing.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(200), EditorDisplay(\"Skinned Model\")") bool ShowDebugDrawSkeleton = false;
|
||||
API_FIELD(Attributes="EditorOrder(200), EditorDisplay(\"Skeleton\"), VisibleIf(nameof(ShowDebugDrawOptions))") bool ShowDebugDrawSkeleton = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
@@ -440,6 +440,16 @@ public:
|
||||
API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
|
||||
|
||||
private:
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Used to hide <see cref="ShowDebugDrawSkeleton"/> options if when the skinned model or animation graph is null.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize") bool GetShowDebugDrawOptions() const
|
||||
{
|
||||
return SkinnedModel != nullptr && AnimationGraph != nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ApplyRootMotion(const Transform& rootMotionDelta);
|
||||
void SyncParameters();
|
||||
void RunBlendShapeDeformer(const MeshBase* mesh, struct MeshDeformationData& deformation);
|
||||
|
||||
@@ -210,7 +210,7 @@ Ray Camera::ConvertMouseToRay(const Float2& mousePosition, const Viewport& viewp
|
||||
{
|
||||
Vector3 position = GetPosition();
|
||||
if (viewport.Width < ZeroTolerance || viewport.Height < ZeroTolerance || mousePosition.IsNaN())
|
||||
return Ray(position, GetDirection());
|
||||
return Ray(position, GetForward());
|
||||
|
||||
// Use different logic in orthographic projection
|
||||
if (!_usePerspective)
|
||||
|
||||
@@ -30,7 +30,7 @@ void DirectionalLight::Draw(RenderContext& renderContext)
|
||||
data.ShadowsDistance = ShadowsDistance;
|
||||
data.Color = Color.ToFloat3() * (Color.A * brightness);
|
||||
data.ShadowsStrength = ShadowsStrength;
|
||||
data.Direction = GetDirection();
|
||||
data.Direction = GetForward();
|
||||
data.ShadowsFadeDistance = ShadowsFadeDistance;
|
||||
data.ShadowsNormalOffsetScale = ShadowsNormalOffsetScale;
|
||||
data.ShadowsDepthBias = ShadowsDepthBias;
|
||||
|
||||
@@ -163,7 +163,7 @@ void ExponentialHeightFog::GetExponentialHeightFogData(const RenderView& view, S
|
||||
result.FogCutoffDistance = FogCutoffDistance >= 0 ? FogCutoffDistance : view.Far + FogCutoffDistance;
|
||||
if (useDirectionalLightInscattering)
|
||||
{
|
||||
result.InscatteringLightDirection = -DirectionalInscatteringLight->GetDirection();
|
||||
result.InscatteringLightDirection = -DirectionalInscatteringLight->GetForward();
|
||||
result.DirectionalInscatteringColor = DirectionalInscatteringColor.ToFloat3();
|
||||
result.DirectionalInscatteringExponent = Math::Clamp(DirectionalInscatteringExponent, 0.000001f, 1000.0f);
|
||||
result.DirectionalInscatteringStartDistance = Math::Min(DirectionalInscatteringStartDistance, view.Far - 1.0f);
|
||||
|
||||
@@ -73,7 +73,7 @@ void Sky::InitConfig(ShaderAtmosphericFogData& config) const
|
||||
|
||||
if (SunLight)
|
||||
{
|
||||
config.AtmosphericFogSunDirection = -SunLight->GetDirection();
|
||||
config.AtmosphericFogSunDirection = -SunLight->GetForward();
|
||||
config.AtmosphericFogSunColor = SunLight->Color.ToFloat3() * SunLight->Color.A;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -497,9 +497,7 @@ void SplineModel::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
SERIALIZE_MEMBER(PreTransform, _preTransform)
|
||||
SERIALIZE(Model);
|
||||
SERIALIZE(DrawModes);
|
||||
|
||||
stream.JKEY("Buffer");
|
||||
stream.Object(&Entries, other ? &other->Entries : nullptr);
|
||||
SERIALIZE_MEMBER(Buffer, Entries);
|
||||
}
|
||||
|
||||
void SplineModel::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
@@ -514,8 +512,7 @@ void SplineModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
|
||||
DESERIALIZE_MEMBER(PreTransform, _preTransform);
|
||||
DESERIALIZE(Model);
|
||||
DESERIALIZE(DrawModes);
|
||||
|
||||
Entries.DeserializeIfExists(stream, "Buffer", modifier);
|
||||
DESERIALIZE_MEMBER(Buffer, Entries);
|
||||
|
||||
// [Deprecated on 07.02.2022, expires on 07.02.2024]
|
||||
if (modifier->EngineBuild <= 6330)
|
||||
|
||||
@@ -27,7 +27,7 @@ SpotLight::SpotLight(const SpawnParams& params)
|
||||
_cosInnerCone = Math::Cos(_innerConeAngle * DegreesToRadians);
|
||||
_invCosConeDifference = 1.0f / (_cosInnerCone - _cosOuterCone);
|
||||
const float boundsRadius = Math::Sqrt(1.25f * _radius * _radius - _radius * _radius * _cosOuterCone);
|
||||
_sphere = BoundingSphere(GetPosition() + 0.5f * GetDirection() * _radius, boundsRadius);
|
||||
_sphere = BoundingSphere(GetPosition() + 0.5f * GetForward() * _radius, boundsRadius);
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ void SpotLight::UpdateBounds()
|
||||
// Note: we use the law of cosines to find the distance to the furthest edge of the spotlight cone from a position that is halfway down the spotlight direction
|
||||
const float radius = GetScaledRadius();
|
||||
const float boundsRadius = Math::Sqrt(1.25f * radius * radius - radius * radius * _cosOuterCone);
|
||||
_sphere = BoundingSphere(GetPosition() + 0.5f * GetDirection() * radius, boundsRadius);
|
||||
_sphere = BoundingSphere(GetPosition() + 0.5f * GetForward() * radius, boundsRadius);
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
|
||||
if (_sceneRenderingKey != -1)
|
||||
@@ -199,7 +199,7 @@ void SpotLight::OnDebugDrawSelected()
|
||||
const auto color = Color::Yellow;
|
||||
Vector3 right = _transform.GetRight();
|
||||
Vector3 up = _transform.GetUp();
|
||||
Vector3 forward = GetDirection();
|
||||
Vector3 forward = GetForward();
|
||||
float radius = GetScaledRadius();
|
||||
float discRadius = radius * Math::Tan(_outerConeAngle * DegreesToRadians);
|
||||
float falloffDiscRadius = radius * Math::Tan(_innerConeAngle * DegreesToRadians);
|
||||
@@ -231,7 +231,7 @@ void SpotLight::DrawLightsDebug(RenderView& view)
|
||||
const auto color = Color::Yellow;
|
||||
Vector3 right = _transform.GetRight();
|
||||
Vector3 up = _transform.GetUp();
|
||||
Vector3 forward = GetDirection();
|
||||
Vector3 forward = GetForward();
|
||||
float radius = GetScaledRadius();
|
||||
float discRadius = radius * Math::Tan(_outerConeAngle * DegreesToRadians);
|
||||
float falloffDiscRadius = radius * Math::Tan(_innerConeAngle * DegreesToRadians);
|
||||
|
||||
@@ -464,8 +464,7 @@ void StaticModel::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
stream.Rectangle(Lightmap.UVsArea);
|
||||
}
|
||||
|
||||
stream.JKEY("Buffer");
|
||||
stream.Object(&Entries, other ? &other->Entries : nullptr);
|
||||
SERIALIZE_MEMBER(Buffer, Entries);
|
||||
|
||||
if (_vertexColorsCount)
|
||||
{
|
||||
@@ -504,8 +503,7 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
|
||||
DESERIALIZE_MEMBER(DrawModes, _drawModes);
|
||||
DESERIALIZE_MEMBER(LightmapIndex, Lightmap.TextureIndex);
|
||||
DESERIALIZE_MEMBER(LightmapArea, Lightmap.UVsArea);
|
||||
|
||||
Entries.DeserializeIfExists(stream, "Buffer", modifier);
|
||||
DESERIALIZE_MEMBER(Buffer, Entries);
|
||||
|
||||
{
|
||||
const auto member = stream.FindMember("VertexColors");
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/MainThreadTask.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
|
||||
// Apply flow:
|
||||
// - collect all prefabs using this prefab (load and create default instances)
|
||||
@@ -772,7 +773,13 @@ bool Prefab::ApplyAll(Actor* targetActor)
|
||||
if (ApplyAllInternal(targetActor, true, thisPrefabInstancesData))
|
||||
return true;
|
||||
|
||||
SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData);
|
||||
// Sync nested prefabs
|
||||
if (allPrefabs.HasItems())
|
||||
{
|
||||
LOG(Info, "Updating referencing prefabs");
|
||||
HashSet<Guid> synced;
|
||||
SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData, synced);
|
||||
}
|
||||
|
||||
const auto endTime = DateTime::NowUTC();
|
||||
LOG(Info, "Prefab updated! {0} ms", (int32)(endTime - startTime).GetTotalMilliseconds());
|
||||
@@ -1027,8 +1034,14 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
rapidjson_flax::Document targetDataDocument;
|
||||
if (NestedPrefabs.HasItems())
|
||||
{
|
||||
// Use initial data buffer (unstripped) but reorder objects to match the sequence (eg. when new object was added to the nested prefab)
|
||||
targetDataDocument.Parse(dataBuffer.GetString(), dataBuffer.GetSize());
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, targetDataDocument, modifier.Value);
|
||||
Array<SceneObject*> reorderedObjects = *sceneObjects.Value;
|
||||
newPrefabInstanceIdToDataIndexCounter = 0;
|
||||
for (auto i = newPrefabInstanceIdToDataIndex.Begin(); i.IsNotEnd(); ++i)
|
||||
reorderedObjects.Insert(i->Value, sceneObjects->At(newPrefabInstanceIdToDataIndexStart + newPrefabInstanceIdToDataIndexCounter++));
|
||||
reorderedObjects.Resize(sceneObjects.Value->Count()); // reorderedObjects matches order in targetDataDocument
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(reorderedObjects, targetDataDocument, modifier.Value);
|
||||
SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData);
|
||||
|
||||
if (context.Instances.HasItems())
|
||||
@@ -1236,7 +1249,7 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
||||
{
|
||||
return Init(TypeName, StringAnsiView(tmpBuffer.GetString(), (int32)tmpBuffer.GetSize()));
|
||||
}
|
||||
#if 1 // Set to 0 to use memory-only reload that does not modifies the source file - useful for testing and debugging prefabs apply
|
||||
#if 1 // Set to 0 to use memory-only reload that does not modify the source file - useful for testing and debugging prefabs apply
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
Locker.Unlock();
|
||||
|
||||
@@ -1295,7 +1308,7 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
||||
_defaultInstance->DeleteObject();
|
||||
_defaultInstance = nullptr;
|
||||
}
|
||||
_isLoaded = false;
|
||||
_loadState = 0;
|
||||
|
||||
// Update prefab data manually (to prevent updating source asset file - just for testing)
|
||||
Document.Parse(buffer.GetString(), buffer.GetSize());
|
||||
@@ -1348,7 +1361,7 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
||||
NestedPrefabs.Add(prefabId);
|
||||
}
|
||||
}
|
||||
_isLoaded = true;
|
||||
_loadState = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1395,34 +1408,31 @@ bool Prefab::SyncChangesInternal(PrefabInstancesData& prefabInstancesData)
|
||||
return ApplyAllInternal(targetActor, false, prefabInstancesData);
|
||||
}
|
||||
|
||||
void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData) const
|
||||
void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData, HashSet<Guid>& synced) const
|
||||
{
|
||||
PROFILE_CPU();
|
||||
LOG(Info, "Updating referencing prefabs");
|
||||
|
||||
// TODO: this may not work well for very complex prefab nesting -> loop order matters, maybe build a graph of dependencies?
|
||||
|
||||
// Call recursive for all referencing prefab assets to refresh nested prefabs
|
||||
for (int32 i = 0; i < allPrefabs.Count(); i++)
|
||||
{
|
||||
auto nestedPrefab = allPrefabs[i].Get();
|
||||
if (nestedPrefab)
|
||||
Prefab* nestedPrefab = allPrefabs[i].Get();
|
||||
if (!nestedPrefab || synced.Contains(nestedPrefab->GetID()))
|
||||
continue;
|
||||
if (nestedPrefab->WaitForLoaded())
|
||||
{
|
||||
if (nestedPrefab->WaitForLoaded())
|
||||
{
|
||||
LOG(Warning, "Waiting for prefab asset load failed.");
|
||||
continue;
|
||||
}
|
||||
LOG(Warning, "Waiting for '{}' load failed.", nestedPrefab->ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sync only if prefab is used by this prefab (directly) and it has been captured before
|
||||
const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID());
|
||||
if (nestedPrefabIndex != -1)
|
||||
{
|
||||
if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[i]))
|
||||
continue;
|
||||
nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData);
|
||||
ObjectsRemovalService::Flush();
|
||||
}
|
||||
// Sync only if prefab is used by this prefab (directly) and it has been captured before
|
||||
const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID());
|
||||
if (nestedPrefabIndex != -1)
|
||||
{
|
||||
synced.Add(nestedPrefab->GetID());
|
||||
if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[i]))
|
||||
continue;
|
||||
nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData, synced);
|
||||
ObjectsRemovalService::Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Level/Prefabs/PrefabManager.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
|
||||
@@ -22,6 +23,7 @@ Prefab::Prefab(const SpawnParams& params, const AssetInfo* info)
|
||||
|
||||
Guid Prefab::GetRootObjectId() const
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ASSERT(IsLoaded());
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
@@ -57,11 +59,12 @@ Actor* Prefab::GetDefaultInstance()
|
||||
// Skip if already created (reuse cached result)
|
||||
if (_defaultInstance)
|
||||
return _defaultInstance;
|
||||
PROFILE_CPU();
|
||||
|
||||
// Skip if not loaded
|
||||
if (!IsLoaded())
|
||||
{
|
||||
LOG(Warning, "Cannot instantiate object from not loaded prefab asset.");
|
||||
LOG(Warning, "Cannot instantiate object from not loaded prefab asset ({}, {})", GetPath(), GetID());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ private:
|
||||
bool ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData);
|
||||
bool UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, rapidjson_flax::StringBuffer& tmpBuffer);
|
||||
bool SyncChangesInternal(PrefabInstancesData& prefabInstancesData);
|
||||
void SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData) const;
|
||||
void SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData, HashSet<Guid, HeapAllocation>& synced) const;
|
||||
#endif
|
||||
void DeleteDefaultInstance();
|
||||
|
||||
|
||||
@@ -752,7 +752,7 @@ void SceneObjectsFactory::SynchronizePrefabInstances(Context& context, PrefabSyn
|
||||
obj->SetOrderInParent(order);
|
||||
}
|
||||
|
||||
// Setup hierarchy for the prefab instances (ensure any new objects are connected)
|
||||
// Setup hierarchy for the prefab instances (after adding new objects to ensure they are connected, eg. when reparenting existing prefab into a new root)
|
||||
for (const auto& instance : context.Instances)
|
||||
{
|
||||
const auto& prefabStartData = data.Data[instance.StatIndex];
|
||||
|
||||
@@ -27,65 +27,41 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* nod
|
||||
switch (param->Type.Type)
|
||||
{
|
||||
case VariantType::Float2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsFloat2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Float3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsFloat3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Float4:
|
||||
case VariantType::Color:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsFloat4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsDouble2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsDouble3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double4:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsDouble4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Int2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsInt2().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Int3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsInt3().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Int4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsInt4().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Matrix:
|
||||
{
|
||||
|
||||
@@ -184,6 +184,15 @@ PhysicsScene* Physics::FindScene(const StringView& name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Physics::DeleteScene(PhysicsScene* scene)
|
||||
{
|
||||
if (scene == nullptr || scene == DefaultScene)
|
||||
return;
|
||||
scene->CollectResults();
|
||||
Scenes.RemoveKeepOrder(scene);
|
||||
Delete(scene);
|
||||
}
|
||||
|
||||
bool Physics::GetAutoSimulation()
|
||||
{
|
||||
return !DefaultScene || DefaultScene->GetAutoSimulation();
|
||||
|
||||
@@ -32,6 +32,11 @@ API_CLASS(Static) class FLAXENGINE_API Physics
|
||||
/// </summary>
|
||||
API_FUNCTION() static PhysicsScene* FindScene(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Delete an existing scene (excluding the default one).
|
||||
/// </summary>
|
||||
API_FUNCTION() static void DeleteScene(PhysicsScene* scene);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The automatic simulation feature. True if perform physics simulation after on fixed update by auto, otherwise user should do it.
|
||||
|
||||
@@ -36,7 +36,7 @@ public class Platform : EngineModule
|
||||
{
|
||||
options.OutputFiles.Add("dbghelp.lib");
|
||||
options.DelayLoadLibraries.Add("dbghelp.dll");
|
||||
options.DependencyFiles.Add(Path.Combine(options.DepsFolder, "dbghelp.dll"));
|
||||
options.OptionalDependencyFiles.Add(Path.Combine(options.DepsFolder, "dbghelp.dll"));
|
||||
}
|
||||
if (options.Target.IsEditor)
|
||||
{
|
||||
|
||||
@@ -75,6 +75,12 @@
|
||||
Platform::CheckFailed(#expression, __FILE__, __LINE__); \
|
||||
return returnValue; \
|
||||
}
|
||||
// Performs a soft check of the expression. Logs the expression failure and continues execution.
|
||||
#define CHECK_NO_RETURN(expression) \
|
||||
if (!(expression)) \
|
||||
{ \
|
||||
Platform::CheckFailed(#expression, __FILE__, __LINE__); \
|
||||
}
|
||||
|
||||
#if ENABLE_ASSERTION
|
||||
// Performs a soft check of the expression. Logs the expression failure and returns from the function call.
|
||||
|
||||
@@ -189,6 +189,7 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM
|
||||
POINT p;
|
||||
p.x = static_cast<LONG>(WINDOWS_GET_X_LPARAM(lParam));
|
||||
p.y = static_cast<LONG>(WINDOWS_GET_Y_LPARAM(lParam));
|
||||
const Float2 mousePosScreen(static_cast<float>(p.x), static_cast<float>(p.y));
|
||||
::ClientToScreen(window->GetHWND(), &p);
|
||||
const Float2 mousePos(static_cast<float>(p.x), static_cast<float>(p.y));
|
||||
|
||||
@@ -203,7 +204,8 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM
|
||||
}
|
||||
case WM_NCMOUSEMOVE:
|
||||
{
|
||||
OnMouseMove(mousePos, window);
|
||||
// The position in the message is already reported in screen-space
|
||||
OnMouseMove(mousePosScreen, window);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -356,9 +356,7 @@ void Renderer::DrawActors(RenderContext& renderContext, const Array<Actor*>& cus
|
||||
Level::DrawActors(renderContextBatch, SceneRendering::DrawCategory::SceneDraw);
|
||||
Level::DrawActors(renderContextBatch, SceneRendering::DrawCategory::SceneDrawAsync);
|
||||
JobSystem::SetJobStartingOnDispatch(true);
|
||||
for (const int64 label : renderContextBatch.WaitLabels)
|
||||
JobSystem::Wait(label);
|
||||
renderContextBatch.WaitLabels.Clear();
|
||||
renderContextBatch.FlushWaitLabels();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,9 +481,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
|
||||
// Wait for async jobs to finish
|
||||
JobSystem::SetJobStartingOnDispatch(true);
|
||||
for (const int64 label : renderContextBatch.WaitLabels)
|
||||
JobSystem::Wait(label);
|
||||
renderContextBatch.WaitLabels.Clear();
|
||||
renderContextBatch.FlushWaitLabels();
|
||||
|
||||
// Perform custom post-scene drawing (eg. GPU dispatches used by VFX)
|
||||
for (int32 i = 0; i < renderContextBatch.Contexts.Count(); i++)
|
||||
@@ -737,7 +733,9 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
}
|
||||
|
||||
// Upscaling after scene rendering but before post processing
|
||||
bool useUpscaling = task->RenderingPercentage < 1.0f;
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
bool useUpscaling = task->RenderingPercentage * task->RenderScale < 1.0f;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
const Viewport outputViewport = task->GetOutputViewport();
|
||||
if (useUpscaling && setup.UpscaleLocation == RenderingUpscaleLocation::BeforePostProcessingPass)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes enum serialization to use string names instead of integer values. This makes saved data resilient to enum reordering or changes in values (but not to renaming enums). Deserialization accepts both string names and integer values for backward compatibility.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Attribute" />
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public sealed class EnumStringAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul
|
||||
Struct.SetField = setField;
|
||||
}
|
||||
|
||||
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items)
|
||||
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items, bool stringSerialization)
|
||||
: ManagedClass(nullptr)
|
||||
, Module(module)
|
||||
, InitRuntime(DefaultInitRuntime)
|
||||
@@ -215,6 +215,7 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul
|
||||
, Size(size)
|
||||
{
|
||||
Enum.Items = items;
|
||||
Enum.StringSerialization = stringSerialization;
|
||||
}
|
||||
|
||||
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, InitRuntimeHandler initRuntime, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable, GetInterfaceWrapper getInterfaceWrapper)
|
||||
@@ -270,6 +271,7 @@ ScriptingType::ScriptingType(const ScriptingType& other)
|
||||
break;
|
||||
case ScriptingTypes::Enum:
|
||||
Enum.Items = other.Enum.Items;
|
||||
Enum.StringSerialization = other.Enum.StringSerialization;
|
||||
break;
|
||||
case ScriptingTypes::Interface:
|
||||
Interface.SetupScriptVTable = other.Interface.SetupScriptVTable;
|
||||
@@ -323,6 +325,7 @@ ScriptingType::ScriptingType(ScriptingType&& other)
|
||||
break;
|
||||
case ScriptingTypes::Enum:
|
||||
Enum.Items = other.Enum.Items;
|
||||
Enum.StringSerialization = other.Enum.StringSerialization;
|
||||
break;
|
||||
case ScriptingTypes::Interface:
|
||||
Interface.SetupScriptVTable = other.Interface.SetupScriptVTable;
|
||||
@@ -604,71 +607,57 @@ StringAnsiView ScriptingType::GetName() const
|
||||
return Fullname;
|
||||
}
|
||||
|
||||
#if BUILD_DEBUG || USE_EDITOR
|
||||
#define INIT_TYPE(...) \
|
||||
module->Types.AddUninitialized(); \
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, ##__VA_ARGS__); \
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname)) \
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName())); \
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
#else
|
||||
#define INIT_TYPE(...) \
|
||||
module->Types.AddUninitialized(); \
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, ##__VA_ARGS__); \
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
#endif
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SpawnHandler spawn, ScriptingTypeInitializer* baseType, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, const ScriptingType::InterfaceImplementation* interfaces)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Script
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, spawn, baseType, setupScriptVTable, setupScriptObjectVTable, interfaces);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(size, initRuntime, spawn, baseType, setupScriptVTable, setupScriptObjectVTable, interfaces);
|
||||
}
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Class
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, baseType, interfaces);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(size, initRuntime, ctor, dtor, baseType, interfaces);
|
||||
}
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Structure
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, copy, box, unbox, getField, setField, baseType, interfaces);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(size, initRuntime, ctor, dtor, copy, box, unbox, getField, setField, baseType, interfaces);
|
||||
}
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items)
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items, bool stringSerialization)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Enum
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, items);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(size, items, stringSerialization);
|
||||
}
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, ScriptingType::GetInterfaceWrapper getInterfaceWrapper)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Interface
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, initRuntime, setupScriptVTable, setupScriptObjectVTable, getInterfaceWrapper);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(initRuntime, setupScriptVTable, setupScriptObjectVTable, getInterfaceWrapper);
|
||||
}
|
||||
|
||||
#undef INIT_TYPE
|
||||
|
||||
CriticalSection BinaryModule::Locker;
|
||||
|
||||
BinaryModule::BinaryModulesList& BinaryModule::GetModules()
|
||||
|
||||
@@ -609,6 +609,12 @@ MObject* MUtils::BoxVariant(const Variant& value)
|
||||
return MCore::Object::Box((void*)&value.AsData, Double3::TypeInitializer.GetClass());
|
||||
case VariantType::Double4:
|
||||
return MCore::Object::Box((void*)&value.AsData, Double4::TypeInitializer.GetClass());
|
||||
case VariantType::Int2:
|
||||
return MCore::Object::Box((void*)&value.AsData, Int2::TypeInitializer.GetClass());
|
||||
case VariantType::Int3:
|
||||
return MCore::Object::Box((void*)&value.AsData, Int3::TypeInitializer.GetClass());
|
||||
case VariantType::Int4:
|
||||
return MCore::Object::Box((void*)&value.AsData, Int4::TypeInitializer.GetClass());
|
||||
case VariantType::Color:
|
||||
return MCore::Object::Box((void*)&value.AsData, stdTypes.ColorClass);
|
||||
case VariantType::Guid:
|
||||
@@ -889,6 +895,12 @@ MClass* MUtils::GetClass(const VariantType& value)
|
||||
return Double3::TypeInitializer.GetClass();
|
||||
case VariantType::Double4:
|
||||
return Double4::TypeInitializer.GetClass();
|
||||
case VariantType::Int2:
|
||||
return Int2::TypeInitializer.GetClass();
|
||||
case VariantType::Int3:
|
||||
return Int3::TypeInitializer.GetClass();
|
||||
case VariantType::Int4:
|
||||
return Int4::TypeInitializer.GetClass();
|
||||
case VariantType::Color:
|
||||
return Color::TypeInitializer.GetClass();
|
||||
case VariantType::Guid:
|
||||
@@ -979,6 +991,12 @@ MClass* MUtils::GetClass(const Variant& value)
|
||||
return Double3::TypeInitializer.GetClass();
|
||||
case VariantType::Double4:
|
||||
return Double4::TypeInitializer.GetClass();
|
||||
case VariantType::Int2:
|
||||
return Int2::TypeInitializer.GetClass();
|
||||
case VariantType::Int3:
|
||||
return Int3::TypeInitializer.GetClass();
|
||||
case VariantType::Int4:
|
||||
return Int4::TypeInitializer.GetClass();
|
||||
case VariantType::Color:
|
||||
return stdTypes.ColorClass;
|
||||
case VariantType::Guid:
|
||||
@@ -1148,6 +1166,9 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
|
||||
CASE_IN_BUILD_TYPE(Double2, AsData);
|
||||
CASE_IN_BUILD_TYPE(Double3, AsData);
|
||||
CASE_IN_BUILD_TYPE(Double4, AsBlob.Data);
|
||||
CASE_IN_BUILD_TYPE(Int2, AsData);
|
||||
CASE_IN_BUILD_TYPE(Int3, AsData);
|
||||
CASE_IN_BUILD_TYPE(Int4, AsData);
|
||||
#undef CASE_IN_BUILD_TYPE
|
||||
if (klass->IsValueType())
|
||||
{
|
||||
|
||||
@@ -1636,7 +1636,7 @@ FORCE_INLINE StringAnsiView GetPropertyMethodName(MProperty* property, StringAns
|
||||
Platform::MemoryCopy(mem, prefix.Get(), prefix.Length());
|
||||
Platform::MemoryCopy(mem + prefix.Length(), name.Get(), name.Length());
|
||||
mem[name.Length() + prefix.Length()] = 0;
|
||||
return StringAnsiView(mem, name.Length() + prefix.Length() + 1);
|
||||
return StringAnsiView(mem, name.Length() + prefix.Length());
|
||||
}
|
||||
|
||||
MProperty::MProperty(MClass* parentClass, const char* name, void* handle, void* getterHandle, void* setterHandle, MMethodAttributes getterAttributes, MMethodAttributes setterAttributes)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user