Merge branch 'master' into 1.8
This commit is contained in:
BIN
Content/Shaders/GlobalSignDistanceField.flax
(Stored with Git LFS)
BIN
Content/Shaders/GlobalSignDistanceField.flax
(Stored with Git LFS)
Binary file not shown.
@@ -220,8 +220,9 @@ namespace FlaxEditor.Content.GUI
|
||||
// Remove references and unlink items
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
_items[i].Parent = null;
|
||||
_items[i].RemoveReference(this);
|
||||
var item = _items[i];
|
||||
item.Parent = null;
|
||||
item.RemoveReference(this);
|
||||
}
|
||||
_items.Clear();
|
||||
|
||||
@@ -263,11 +264,12 @@ namespace FlaxEditor.Content.GUI
|
||||
// Add references and link items
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i].Visible)
|
||||
var item = items[i];
|
||||
if (item.Visible && !_items.Contains(item))
|
||||
{
|
||||
items[i].Parent = this;
|
||||
items[i].AddReference(this);
|
||||
_items.Add(items[i]);
|
||||
item.Parent = this;
|
||||
item.AddReference(this);
|
||||
_items.Add(item);
|
||||
}
|
||||
}
|
||||
if (selection != null)
|
||||
@@ -279,6 +281,8 @@ namespace FlaxEditor.Content.GUI
|
||||
// Sort items depending on sortMethod parameter
|
||||
_children.Sort(((control, control1) =>
|
||||
{
|
||||
if (control == null || control1 == null)
|
||||
return 0;
|
||||
if (sortType == SortType.AlphabeticReverse)
|
||||
{
|
||||
if (control.CompareTo(control1) > 0)
|
||||
|
||||
@@ -323,8 +323,6 @@ namespace FlaxEditor.Content
|
||||
/// <param name="value">The new path.</param>
|
||||
internal virtual void UpdatePath(string value)
|
||||
{
|
||||
Assert.AreNotEqual(Path, value);
|
||||
|
||||
// Set path
|
||||
Path = StringUtils.NormalizePath(value);
|
||||
FileName = System.IO.Path.GetFileName(value);
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace FlaxEditor.Content
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Texture)request.Asset);
|
||||
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((CubeTexture)request.Asset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -30,6 +30,12 @@ namespace FlaxEditor.Content
|
||||
return item is SceneItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AcceptsAsset(string typeName, string path)
|
||||
{
|
||||
return (typeName == Scene.AssetTypename || typeName == Scene.EditorPickerTypename) && path.EndsWith(FileExtension, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
|
||||
@@ -406,18 +406,16 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
for (int i = 0; i < maxChecks; i++)
|
||||
{
|
||||
var request = _requests[i];
|
||||
|
||||
try
|
||||
{
|
||||
if (request.IsReady)
|
||||
{
|
||||
return request;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogWarning(ex);
|
||||
Editor.LogWarning($"Failed to prepare thumbnail rendering for {request.Item.ShortName}.");
|
||||
Editor.LogWarning(ex);
|
||||
_requests.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,7 +513,6 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
for (int i = 0; i < checks; i++)
|
||||
{
|
||||
var request = _requests[i];
|
||||
|
||||
try
|
||||
{
|
||||
if (request.IsReady)
|
||||
@@ -529,8 +526,9 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogWarning(ex);
|
||||
Editor.LogWarning($"Failed to prepare thumbnail rendering for {request.Item.ShortName}.");
|
||||
Editor.LogWarning(ex);
|
||||
_requests.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,8 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
if (FileSystem::DirectoryExists(dstDotnet))
|
||||
{
|
||||
String cachedData;
|
||||
File::ReadAllText(dotnetCacheFilePath, cachedData);
|
||||
if (FileSystem::FileExists(dotnetCacheFilePath))
|
||||
File::ReadAllText(dotnetCacheFilePath, cachedData);
|
||||
if (cachedData != dotnetCachedValue)
|
||||
{
|
||||
FileSystem::DeleteDirectory(dstDotnet);
|
||||
@@ -360,7 +361,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
data.AddRootEngineAsset(PRE_INTEGRATED_GF_ASSET_NAME);
|
||||
data.AddRootEngineAsset(SMAA_AREA_TEX);
|
||||
data.AddRootEngineAsset(SMAA_SEARCH_TEX);
|
||||
if (data.Configuration != BuildConfiguration::Release)
|
||||
if (!buildSettings.SkipDefaultFonts)
|
||||
data.AddRootEngineAsset(TEXT("Editor/Fonts/Roboto-Regular"));
|
||||
|
||||
// Register custom assets (eg. plugins)
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return;
|
||||
var gizmos = gizmoOwner.Gizmos;
|
||||
_gizmoMode = new ClothPaintingGizmoMode();
|
||||
|
||||
|
||||
var projectCache = Editor.Instance.ProjectCache;
|
||||
if (projectCache.TryGetCustomData("ClothGizmoPaintValue", out var cachedPaintValue))
|
||||
_gizmoMode.PaintValue = JsonSerializer.Deserialize<float>(cachedPaintValue);
|
||||
@@ -48,7 +48,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_gizmoMode.BrushSize = JsonSerializer.Deserialize<float>(cachedBrushSize);
|
||||
if (projectCache.TryGetCustomData("ClothGizmoBrushStrength", out var cachedBrushStrength))
|
||||
_gizmoMode.BrushStrength = JsonSerializer.Deserialize<float>(cachedBrushStrength);
|
||||
|
||||
|
||||
gizmos.AddMode(_gizmoMode);
|
||||
_prevMode = gizmos.ActiveMode;
|
||||
gizmos.ActiveMode = _gizmoMode;
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Actions;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated;
|
||||
|
||||
@@ -10,6 +17,10 @@ namespace FlaxEditor.CustomEditors.Dedicated;
|
||||
[CustomEditor(typeof(MissingScript)), DefaultEditor]
|
||||
public class MissingScriptEditor : GenericEditor
|
||||
{
|
||||
private DropPanel _dropPanel;
|
||||
private Button _replaceScriptButton;
|
||||
private CheckBox _shouldReplaceAllCheckbox;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
@@ -18,9 +29,137 @@ public class MissingScriptEditor : GenericEditor
|
||||
base.Initialize(layout);
|
||||
return;
|
||||
}
|
||||
_dropPanel = dropPanel;
|
||||
_dropPanel.HeaderTextColor = Color.OrangeRed;
|
||||
|
||||
dropPanel.HeaderTextColor = Color.OrangeRed;
|
||||
var replaceScriptPanel = new Panel
|
||||
{
|
||||
Parent = _dropPanel,
|
||||
Height = 64,
|
||||
};
|
||||
|
||||
_replaceScriptButton = new Button
|
||||
{
|
||||
Text = "Replace Script",
|
||||
TooltipText = "Replaces the missing script with a given script type",
|
||||
AnchorPreset = AnchorPresets.TopCenter,
|
||||
Width = 240,
|
||||
Height = 24,
|
||||
X = -120,
|
||||
Y = 0,
|
||||
Parent = replaceScriptPanel,
|
||||
};
|
||||
_replaceScriptButton.Clicked += OnReplaceScriptButtonClicked;
|
||||
|
||||
var replaceAllLabel = new Label
|
||||
{
|
||||
Text = "Replace all matching missing scripts",
|
||||
TooltipText = "Whether or not to apply this script change to all scripts missing the same type.",
|
||||
AnchorPreset = AnchorPresets.BottomCenter,
|
||||
Y = -34,
|
||||
Parent = replaceScriptPanel,
|
||||
};
|
||||
replaceAllLabel.X -= FlaxEngine.GUI.Style.Current.FontSmall.MeasureText(replaceAllLabel.Text).X;
|
||||
|
||||
_shouldReplaceAllCheckbox = new CheckBox
|
||||
{
|
||||
TooltipText = replaceAllLabel.TooltipText,
|
||||
AnchorPreset = AnchorPresets.BottomCenter,
|
||||
Y = -34,
|
||||
Parent = replaceScriptPanel,
|
||||
};
|
||||
|
||||
float centerDifference = (_shouldReplaceAllCheckbox.Right - replaceAllLabel.Left) / 2;
|
||||
replaceAllLabel.X += centerDifference;
|
||||
_shouldReplaceAllCheckbox.X += centerDifference;
|
||||
|
||||
base.Initialize(layout);
|
||||
}
|
||||
|
||||
private void FindActorsWithMatchingMissingScript(List<MissingScript> missingScripts)
|
||||
{
|
||||
foreach (Actor actor in Level.GetActors(typeof(Actor)))
|
||||
{
|
||||
for (int scriptIndex = 0; scriptIndex < actor.ScriptsCount; scriptIndex++)
|
||||
{
|
||||
Script actorScript = actor.Scripts[scriptIndex];
|
||||
if (actorScript is not MissingScript missingActorScript)
|
||||
continue;
|
||||
|
||||
MissingScript currentMissing = Values[0] as MissingScript;
|
||||
if (missingActorScript.MissingTypeName != currentMissing.MissingTypeName)
|
||||
continue;
|
||||
|
||||
missingScripts.Add(missingActorScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RunReplacementMultiCast(List<IUndoAction> actions)
|
||||
{
|
||||
if (actions.Count == 0)
|
||||
{
|
||||
Editor.LogWarning("Failed to replace scripts!");
|
||||
return;
|
||||
}
|
||||
|
||||
var multiAction = new MultiUndoAction(actions);
|
||||
multiAction.Do();
|
||||
var presenter = ParentEditor.Presenter;
|
||||
if (presenter != null)
|
||||
{
|
||||
presenter.Undo.AddAction(multiAction);
|
||||
presenter.Control.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceScript(ScriptType script, bool replaceAllInScene)
|
||||
{
|
||||
var actions = new List<IUndoAction>(4);
|
||||
|
||||
var missingScripts = new List<MissingScript>();
|
||||
if (!replaceAllInScene)
|
||||
missingScripts.Add((MissingScript)Values[0]);
|
||||
else
|
||||
FindActorsWithMatchingMissingScript(missingScripts);
|
||||
|
||||
foreach (var missingScript in missingScripts)
|
||||
actions.Add(AddRemoveScript.Add(missingScript.Actor, script));
|
||||
RunReplacementMultiCast(actions);
|
||||
|
||||
for (int actionIdx = 0; actionIdx < actions.Count; actionIdx++)
|
||||
{
|
||||
AddRemoveScript addRemoveScriptAction = (AddRemoveScript)actions[actionIdx];
|
||||
int orderInParent = addRemoveScriptAction.GetOrderInParent();
|
||||
|
||||
Script newScript = missingScripts[actionIdx].Actor.Scripts[orderInParent];
|
||||
missingScripts[actionIdx].ReferenceScript = newScript;
|
||||
}
|
||||
actions.Clear();
|
||||
|
||||
foreach (var missingScript in missingScripts)
|
||||
actions.Add(AddRemoveScript.Remove(missingScript));
|
||||
RunReplacementMultiCast(actions);
|
||||
}
|
||||
|
||||
private void OnReplaceScriptButtonClicked()
|
||||
{
|
||||
var scripts = Editor.Instance.CodeEditing.Scripts.Get();
|
||||
if (scripts.Count == 0)
|
||||
{
|
||||
// No scripts
|
||||
var cm1 = new ContextMenu();
|
||||
cm1.AddButton("No scripts in project");
|
||||
cm1.Show(_dropPanel, _replaceScriptButton.BottomLeft);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show context menu with list of scripts to add
|
||||
var cm = new ItemsListContextMenu(180);
|
||||
for (int i = 0; i < scripts.Count; i++)
|
||||
cm.AddItem(new TypeSearchPopup.TypeItemView(scripts[i]));
|
||||
cm.ItemClicked += item => ReplaceScript((ScriptType)item.Tag, _shouldReplaceAllCheckbox.Checked);
|
||||
cm.SortItems();
|
||||
cm.Show(_dropPanel, _replaceScriptButton.BottomLeft - new Float2((cm.Width - _replaceScriptButton.Width) / 2, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace FlaxEditor.CustomEditors.Elements
|
||||
/// <summary>
|
||||
/// [Deprecated on 26.05.2022, expires on 26.05.2024]
|
||||
/// </summary>
|
||||
[System.Obsolete("Deprecated in 1.4")]
|
||||
[System.Obsolete("Deprecated in 1.4, use ValueBox instead")]
|
||||
public DoubleValueBox DoubleValue => ValueBox;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace FlaxEditor.CustomEditors.Elements
|
||||
/// <summary>
|
||||
/// [Deprecated on 26.05.2022, expires on 26.05.2024]
|
||||
/// </summary>
|
||||
[System.Obsolete("Deprecated in 1.4, ValueBox instead")]
|
||||
[System.Obsolete("Deprecated in 1.4, use ValueBox instead")]
|
||||
public FloatValueBox FloatValue => ValueBox;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -364,7 +364,7 @@ namespace FlaxEditor
|
||||
{
|
||||
foreach (var preview in activePreviews)
|
||||
{
|
||||
if (preview == loadingPreview ||
|
||||
if (preview == loadingPreview ||
|
||||
(preview.Instance != null && (preview.Instance == control || preview.Instance.HasActorInHierarchy(control))))
|
||||
{
|
||||
// Link it to the prefab preview to see it in the editor
|
||||
|
||||
@@ -482,8 +482,8 @@ namespace FlaxEditor.GUI
|
||||
Focus();
|
||||
});
|
||||
if (_selected != null)
|
||||
{
|
||||
var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path);
|
||||
{
|
||||
var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path);
|
||||
popup.ScrollToAndHighlightItemByName(selectedAssetName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Hide parent CM popups and set itself as child
|
||||
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
|
||||
@@ -319,7 +319,9 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
protected override void OnShow()
|
||||
{
|
||||
// Auto cancel on lost focus
|
||||
#if !PLATFORM_LINUX
|
||||
((WindowRootControl)Root).Window.LostFocus += OnCancel;
|
||||
#endif
|
||||
|
||||
base.OnShow();
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
if (Root != null)
|
||||
{
|
||||
bool shiftDown = Root.GetKey(KeyboardKeys.Shift);
|
||||
Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next);
|
||||
Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace FlaxEditor.GUI.Input
|
||||
: this(false, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Init search box
|
||||
/// </summary>
|
||||
@@ -28,7 +28,7 @@ namespace FlaxEditor.GUI.Input
|
||||
: base(isMultiline, x, y, width)
|
||||
{
|
||||
WatermarkText = "Search...";
|
||||
|
||||
|
||||
ClearSearchButton = new Button
|
||||
{
|
||||
Parent = this,
|
||||
|
||||
@@ -182,6 +182,7 @@ namespace FlaxEditor.GUI.Input
|
||||
}
|
||||
SlidingEnd?.Invoke();
|
||||
Defocus();
|
||||
Parent?.Focus();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -241,7 +241,7 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
DoubleClick?.Invoke();
|
||||
RowDoubleClick?.Invoke(this);
|
||||
|
||||
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
|
||||
@@ -191,6 +191,8 @@ namespace FlaxEditor.GUI.Tabs
|
||||
get => _autoTabsSizeAuto;
|
||||
set
|
||||
{
|
||||
if (_autoTabsSizeAuto == value)
|
||||
return;
|
||||
_autoTabsSizeAuto = value;
|
||||
PerformLayout();
|
||||
}
|
||||
@@ -204,11 +206,11 @@ namespace FlaxEditor.GUI.Tabs
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
if (_orientation == value)
|
||||
return;
|
||||
_orientation = value;
|
||||
|
||||
if (UseScroll)
|
||||
TabsPanel.ScrollBars = _orientation == Orientation.Horizontal ? ScrollBars.Horizontal : ScrollBars.Vertical;
|
||||
|
||||
PerformLayout();
|
||||
}
|
||||
}
|
||||
@@ -402,6 +404,14 @@ namespace FlaxEditor.GUI.Tabs
|
||||
tabHeader.Size = tabsSize;
|
||||
}
|
||||
}
|
||||
else if (UseScroll)
|
||||
{
|
||||
// If scroll bar is visible it covers part of the tab header so include this in tab size to improve usability
|
||||
if (_orientation == Orientation.Horizontal && TabsPanel.HScrollBar.Visible)
|
||||
tabsSize.Y += TabsPanel.HScrollBar.Height;
|
||||
else if (_orientation == Orientation.Vertical && TabsPanel.VScrollBar.Visible)
|
||||
tabsSize.X += TabsPanel.VScrollBar.Width;
|
||||
}
|
||||
|
||||
// Fit the tabs panel
|
||||
TabsPanel.Size = _orientation == Orientation.Horizontal
|
||||
|
||||
@@ -11,6 +11,11 @@ namespace FlaxEditor.Gizmo
|
||||
[HideInEditor]
|
||||
public interface IGizmoOwner
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the gizmos collection.
|
||||
/// </summary>
|
||||
FlaxEditor.Viewport.EditorViewport Viewport { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the gizmos collection.
|
||||
/// </summary>
|
||||
|
||||
@@ -162,10 +162,23 @@ namespace FlaxEditor.Gizmo
|
||||
|
||||
// Scale gizmo to fit on-screen
|
||||
Vector3 position = Position;
|
||||
Vector3 vLength = Owner.ViewPosition - position;
|
||||
float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize;
|
||||
_screenScale = (float)(vLength.Length / GizmoScaleFactor * gizmoSize);
|
||||
|
||||
if (Owner.Viewport.UseOrthographicProjection)
|
||||
{
|
||||
//[hack] this is far form ideal the View Position is in wrong location, any think using the View Position will have problem
|
||||
//the camera system needs rewrite the to be a camera on springarm, similar how the ArcBallCamera is handled
|
||||
//the ortho projection cannot exist with fps camera because there is no
|
||||
// - focus point to calculate correct View Position with Orthographic Scale as a reference and Orthographic Scale from View Position
|
||||
// with make the camera jump
|
||||
// - and deaph so w and s movment in orto mode moves the cliping plane now
|
||||
float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize;
|
||||
_screenScale = gizmoSize * (50 * Owner.Viewport.OrthographicScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 vLength = Owner.ViewPosition - position;
|
||||
float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize;
|
||||
_screenScale = (float)(vLength.Length / GizmoScaleFactor * gizmoSize);
|
||||
}
|
||||
// Setup world
|
||||
Quaternion orientation = GetSelectedObject(0).Orientation;
|
||||
_gizmoWorld = new Transform(position, orientation, new Float3(_screenScale));
|
||||
|
||||
@@ -649,8 +649,6 @@ namespace FlaxEditor.Modules
|
||||
// Special case for folders
|
||||
if (item is ContentFolder folder)
|
||||
{
|
||||
// TODO: maybe don't remove folders recursive but at once?
|
||||
|
||||
// Delete all children
|
||||
if (folder.Children.Count > 0)
|
||||
{
|
||||
@@ -664,6 +662,9 @@ namespace FlaxEditor.Modules
|
||||
// Remove directory
|
||||
if (deletedByUser && Directory.Exists(path))
|
||||
{
|
||||
// Flush files removal before removing folder (loaded assets remove file during object destruction in Asset::OnDeleteObject)
|
||||
FlaxEngine.Scripting.FlushRemovedObjects();
|
||||
|
||||
try
|
||||
{
|
||||
Directory.Delete(path, true);
|
||||
@@ -810,10 +811,9 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
if (node == null)
|
||||
return;
|
||||
|
||||
// Temporary data
|
||||
var folder = node.Folder;
|
||||
var path = folder.Path;
|
||||
var canHaveAssets = node.CanHaveAssets;
|
||||
|
||||
if (_isDuringFastSetup)
|
||||
{
|
||||
@@ -832,20 +832,38 @@ namespace FlaxEditor.Modules
|
||||
var child = folder.Children[i];
|
||||
if (!child.Exists)
|
||||
{
|
||||
// Send info
|
||||
// Item doesn't exist anymore
|
||||
Editor.Log(string.Format($"Content item \'{child.Path}\' has been removed"));
|
||||
|
||||
// Destroy it
|
||||
Delete(child, false);
|
||||
|
||||
i--;
|
||||
}
|
||||
else if (canHaveAssets && child is AssetItem childAsset)
|
||||
{
|
||||
// Check if asset type doesn't match the item proxy (eg. item reimported as Material Instance instead of Material)
|
||||
if (FlaxEngine.Content.GetAssetInfo(child.Path, out var assetInfo))
|
||||
{
|
||||
bool changed = assetInfo.ID != childAsset.ID;
|
||||
if (!changed && assetInfo.TypeName != childAsset.TypeName)
|
||||
{
|
||||
// Use proxy check (eg. scene asset might accept different typename than AssetInfo reports)
|
||||
var proxy = GetAssetProxy(childAsset.TypeName, child.Path);
|
||||
if (proxy == null)
|
||||
proxy = GetAssetProxy(assetInfo.TypeName, child.Path);
|
||||
changed = !proxy.AcceptsAsset(assetInfo.TypeName, child.Path);
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
OnAssetTypeInfoChanged(childAsset, ref assetInfo);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find files
|
||||
var files = Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly);
|
||||
if (node.CanHaveAssets)
|
||||
if (canHaveAssets)
|
||||
{
|
||||
LoadAssets(node, files);
|
||||
}
|
||||
@@ -1134,17 +1152,19 @@ namespace FlaxEditor.Modules
|
||||
|
||||
RebuildInternal();
|
||||
|
||||
Editor.ContentImporting.ImportFileEnd += ContentImporting_ImportFileDone;
|
||||
Editor.ContentImporting.ImportFileEnd += (obj, failed) =>
|
||||
{
|
||||
var path = obj.ResultUrl;
|
||||
if (!failed)
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() => OnImportFileDone(path));
|
||||
};
|
||||
_enableEvents = true;
|
||||
}
|
||||
|
||||
private void ContentImporting_ImportFileDone(IFileEntryAction obj, bool failed)
|
||||
private void OnImportFileDone(string path)
|
||||
{
|
||||
if (failed)
|
||||
return;
|
||||
|
||||
// Check if already has that element
|
||||
var item = Find(obj.ResultUrl);
|
||||
var item = Find(path);
|
||||
if (item is BinaryAssetItem binaryAssetItem)
|
||||
{
|
||||
// Get asset info from the registry (content layer will update cache it just after import)
|
||||
@@ -1154,19 +1174,8 @@ namespace FlaxEditor.Modules
|
||||
// For eg. change texture to sprite atlas on reimport
|
||||
if (binaryAssetItem.TypeName != assetInfo.TypeName)
|
||||
{
|
||||
// Asset type has been changed!
|
||||
Editor.LogWarning(string.Format("Asset \'{0}\' changed type from {1} to {2}", item.Path, binaryAssetItem.TypeName, assetInfo.TypeName));
|
||||
Editor.Windows.CloseAllEditors(item);
|
||||
|
||||
// Remove this item from the database and some related data
|
||||
var toRefresh = binaryAssetItem.ParentFolder;
|
||||
binaryAssetItem.Dispose();
|
||||
toRefresh.Children.Remove(binaryAssetItem);
|
||||
if (!binaryAssetItem.HasDefaultThumbnail)
|
||||
{
|
||||
// Delete old thumbnail and remove it from the cache
|
||||
Editor.Instance.Thumbnails.DeletePreview(binaryAssetItem);
|
||||
}
|
||||
OnAssetTypeInfoChanged(binaryAssetItem, ref assetInfo);
|
||||
|
||||
// Refresh the parent folder to find the new asset (it should have different type or some other format)
|
||||
RefreshFolder(toRefresh, false);
|
||||
@@ -1183,6 +1192,23 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAssetTypeInfoChanged(AssetItem assetItem, ref AssetInfo assetInfo)
|
||||
{
|
||||
// Asset type has been changed!
|
||||
Editor.LogWarning(string.Format("Asset \'{0}\' changed type from {1} to {2}", assetItem.Path, assetItem.TypeName, assetInfo.TypeName));
|
||||
Editor.Windows.CloseAllEditors(assetItem);
|
||||
|
||||
// Remove this item from the database and some related data
|
||||
assetItem.Dispose();
|
||||
assetItem.ParentFolder.Children.Remove(assetItem);
|
||||
|
||||
// Delete old thumbnail and remove it from the cache
|
||||
if (!assetItem.HasDefaultThumbnail)
|
||||
{
|
||||
Editor.Instance.Thumbnails.DeletePreview(assetItem);
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnDirectoryEvent(MainContentTreeNode node, FileSystemEventArgs e)
|
||||
{
|
||||
// Ensure to be ready for external events
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace FlaxEditor.Modules
|
||||
hint = "Too long name.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (item.IsFolder && shortName.EndsWith("."))
|
||||
{
|
||||
hint = "Name cannot end with '.'";
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace FlaxEditor.Modules
|
||||
public event Action ImportingQueueBegin;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when file is being imported.
|
||||
/// Occurs when file is being imported. Can be called on non-main thread.
|
||||
/// </summary>
|
||||
public event Action<IFileEntryAction> ImportFileBegin;
|
||||
|
||||
@@ -67,12 +67,12 @@ namespace FlaxEditor.Modules
|
||||
public delegate void ImportFileEndDelegate(IFileEntryAction entry, bool failed);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when file importing end.
|
||||
/// Occurs when file importing end. Can be called on non-main thread.
|
||||
/// </summary>
|
||||
public event ImportFileEndDelegate ImportFileEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when assets importing ends.
|
||||
/// Occurs when assets importing ends. Can be called on non-main thread.
|
||||
/// </summary>
|
||||
public event Action ImportingQueueEnd;
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace FlaxEditor.Modules
|
||||
return;
|
||||
var actorsList = new List<Actor>();
|
||||
Utilities.Utils.GetActorsTree(actorsList, actor);
|
||||
|
||||
|
||||
var actions = new IUndoAction[actorsList.Count];
|
||||
for (int i = 0; i < actorsList.Count; i++)
|
||||
actions[i] = BreakPrefabLinkAction.Linked(actorsList[i]);
|
||||
|
||||
@@ -453,7 +453,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
Editor.Windows.SceneWin.Focus();
|
||||
}
|
||||
|
||||
|
||||
// fix scene window layout
|
||||
Editor.Windows.SceneWin.PerformLayout();
|
||||
Editor.Windows.SceneWin.PerformLayout();
|
||||
@@ -520,7 +520,7 @@ namespace FlaxEditor.Modules
|
||||
Undo.AddAction(new MultiUndoAction(pasteAction, selectAction));
|
||||
OnSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
// Scroll to new selected node while pasting
|
||||
Editor.Windows.SceneWin.ScrollToSelectedNode();
|
||||
}
|
||||
@@ -620,7 +620,7 @@ namespace FlaxEditor.Modules
|
||||
Undo.AddAction(new MultiUndoAction(undoActions));
|
||||
OnSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
// Scroll to new selected node while duplicating
|
||||
Editor.Windows.SceneWin.ScrollToSelectedNode();
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ namespace FlaxEditor.Modules
|
||||
continue;
|
||||
scenes.Add(s);
|
||||
}
|
||||
|
||||
|
||||
// In play-mode Editor mocks the level streaming script
|
||||
if (Editor.IsPlayMode)
|
||||
{
|
||||
|
||||
@@ -29,10 +29,10 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
|
||||
private static bool CheckFunc(ScriptType scriptType)
|
||||
{
|
||||
if (scriptType.IsStatic ||
|
||||
scriptType.IsGenericType ||
|
||||
!scriptType.IsPublic ||
|
||||
scriptType.HasAttribute(typeof(HideInEditorAttribute), true) ||
|
||||
if (scriptType.IsStatic ||
|
||||
scriptType.IsGenericType ||
|
||||
!scriptType.IsPublic ||
|
||||
scriptType.HasAttribute(typeof(HideInEditorAttribute), true) ||
|
||||
scriptType.HasAttribute(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false))
|
||||
return false;
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
@@ -410,9 +410,11 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
base.OnUpdate();
|
||||
|
||||
// Automatic project files generation after workspace modifications
|
||||
if (_autoGenerateScriptsProjectFiles && ScriptsBuilder.IsSourceWorkspaceDirty)
|
||||
if (_autoGenerateScriptsProjectFiles && ScriptsBuilder.IsSourceWorkspaceDirty && !ScriptsBuilder.IsCompiling)
|
||||
{
|
||||
Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
|
||||
// Try to delay generation when a lot of files are added at once
|
||||
if (ScriptsBuilder.IsSourceDirtyFor(TimeSpan.FromMilliseconds(150)))
|
||||
Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI;
|
||||
@@ -11,7 +10,6 @@ using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Progress.Handlers;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.Utilities;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Windows;
|
||||
@@ -208,6 +206,7 @@ namespace FlaxEditor.Modules
|
||||
_toolStripScale.Checked = gizmoMode == TransformGizmoBase.Mode.Scale;
|
||||
//
|
||||
_toolStripBuildScenes.Enabled = (canEditScene && !isPlayMode) || Editor.StateMachine.BuildingScenesState.IsActive;
|
||||
_toolStripBuildScenes.Visible = Editor.Options.Options.General.BuildActions?.Length != 0;
|
||||
_toolStripCook.Enabled = Editor.Windows.GameCookerWin.CanBuild(Platform.PlatformType) && !GameCooker.IsRunning;
|
||||
//
|
||||
var play = _toolStripPlay;
|
||||
@@ -299,7 +298,7 @@ namespace FlaxEditor.Modules
|
||||
else
|
||||
text = "Ready";
|
||||
|
||||
if(ProgressVisible)
|
||||
if (ProgressVisible)
|
||||
{
|
||||
color = Style.Current.Statusbar.Loading;
|
||||
}
|
||||
@@ -402,7 +401,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
UpdateStatusBar();
|
||||
}
|
||||
else if(ProgressVisible)
|
||||
else if (ProgressVisible)
|
||||
{
|
||||
UpdateStatusBar();
|
||||
}
|
||||
@@ -557,7 +556,7 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Game Settings", () =>
|
||||
{
|
||||
var item = Editor.ContentDatabase.Find(GameSettings.GameSettingsAssetPath);
|
||||
if(item != null)
|
||||
if (item != null)
|
||||
Editor.ContentEditing.Open(item);
|
||||
});
|
||||
|
||||
@@ -653,7 +652,7 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Information about Flax", () => new AboutDialog().Show());
|
||||
}
|
||||
|
||||
private void OnOptionsChanged(FlaxEditor.Options.EditorOptions options)
|
||||
private void OnOptionsChanged(EditorOptions options)
|
||||
{
|
||||
var inputOptions = options.Input;
|
||||
|
||||
@@ -688,6 +687,8 @@ namespace FlaxEditor.Modules
|
||||
_menuToolsTakeScreenshot.ShortKeys = inputOptions.TakeScreenshot.ToString();
|
||||
|
||||
MainMenuShortcutKeysUpdated?.Invoke();
|
||||
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
private void InitToolstrip(RootControl mainWindow)
|
||||
@@ -709,11 +710,11 @@ namespace FlaxEditor.Modules
|
||||
_toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip($"Change Gizmo tool mode to Scale ({inputOptions.ScaleMode})");
|
||||
ToolStrip.AddSeparator();
|
||||
|
||||
// Cook scenes
|
||||
// Build scenes
|
||||
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip($"Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options ({inputOptions.BuildScenesData})");
|
||||
|
||||
// Cook and run
|
||||
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.Play})");
|
||||
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.CookAndRun})");
|
||||
_toolStripCook.ContextMenu = new ContextMenu();
|
||||
_toolStripCook.ContextMenu.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked);
|
||||
_toolStripCook.ContextMenu.AddSeparator();
|
||||
|
||||
@@ -276,9 +276,6 @@ namespace FlaxEditor.Modules
|
||||
|
||||
// Get metadata
|
||||
int version = int.Parse(root.Attributes["Version"].Value, CultureInfo.InvariantCulture);
|
||||
var virtualDesktopBounds = Platform.VirtualDesktopBounds;
|
||||
var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location;
|
||||
var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight;
|
||||
|
||||
switch (version)
|
||||
{
|
||||
@@ -288,31 +285,9 @@ namespace FlaxEditor.Modules
|
||||
if (MainWindow)
|
||||
{
|
||||
var mainWindowNode = root["MainWindow"];
|
||||
Rectangle bounds = LoadBounds(mainWindowNode["Bounds"]);
|
||||
bool isMaximized = bool.Parse(mainWindowNode.GetAttribute("IsMaximized"));
|
||||
|
||||
// Clamp position to match current desktop dimensions (if window was on desktop that is now inactive)
|
||||
if (bounds.X < virtualDesktopSafeLeftCorner.X || bounds.Y < virtualDesktopSafeLeftCorner.Y || bounds.X > virtualDesktopSafeRightCorner.X || bounds.Y > virtualDesktopSafeRightCorner.Y)
|
||||
bounds.Location = virtualDesktopSafeLeftCorner;
|
||||
|
||||
if (isMaximized)
|
||||
{
|
||||
if (MainWindow.IsMaximized)
|
||||
MainWindow.Restore();
|
||||
MainWindow.ClientPosition = bounds.Location;
|
||||
MainWindow.Maximize();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Mathf.Min(bounds.Size.X, bounds.Size.Y) >= 1)
|
||||
{
|
||||
MainWindow.ClientBounds = bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainWindow.ClientPosition = bounds.Location;
|
||||
}
|
||||
}
|
||||
bool isMaximized = true, isMinimized = false;
|
||||
Rectangle bounds = LoadBounds(mainWindowNode, ref isMaximized, ref isMinimized);
|
||||
LoadWindow(MainWindow, ref bounds, isMaximized, false);
|
||||
}
|
||||
|
||||
// Load master panel structure
|
||||
@@ -332,11 +307,13 @@ namespace FlaxEditor.Modules
|
||||
continue;
|
||||
|
||||
// Get window properties
|
||||
Rectangle bounds = LoadBounds(child["Bounds"]);
|
||||
bool isMaximized = false, isMinimized = false;
|
||||
Rectangle bounds = LoadBounds(child, ref isMaximized, ref isMinimized);
|
||||
|
||||
// Create window and floating dock panel
|
||||
var window = FloatWindowDockPanel.CreateFloatWindow(MainWindow.GUI, bounds.Location, bounds.Size, WindowStartPosition.Manual, string.Empty);
|
||||
var panel = new FloatWindowDockPanel(masterPanel, window.GUI);
|
||||
LoadWindow(panel.Window.Window, ref bounds, isMaximized, isMinimized);
|
||||
|
||||
// Load structure
|
||||
LoadPanel(child, panel);
|
||||
@@ -493,23 +470,67 @@ namespace FlaxEditor.Modules
|
||||
|
||||
private static void SaveBounds(XmlWriter writer, Window win)
|
||||
{
|
||||
var bounds = win.ClientBounds;
|
||||
|
||||
writer.WriteAttributeString("X", bounds.X.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Y", bounds.Y.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Width", bounds.Width.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Height", bounds.Height.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteStartElement("Bounds");
|
||||
{
|
||||
var bounds = win.ClientBounds;
|
||||
writer.WriteAttributeString("X", bounds.X.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Y", bounds.Y.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Width", bounds.Width.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("Height", bounds.Height.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteAttributeString("IsMaximized", win.IsMaximized.ToString());
|
||||
writer.WriteAttributeString("IsMinimized", win.IsMinimized.ToString());
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
private static Rectangle LoadBounds(XmlElement node)
|
||||
private static Rectangle LoadBounds(XmlElement node, ref bool isMaximized, ref bool isMinimized)
|
||||
{
|
||||
float x = float.Parse(node.GetAttribute("X"), CultureInfo.InvariantCulture);
|
||||
float y = float.Parse(node.GetAttribute("Y"), CultureInfo.InvariantCulture);
|
||||
float width = float.Parse(node.GetAttribute("Width"), CultureInfo.InvariantCulture);
|
||||
float height = float.Parse(node.GetAttribute("Height"), CultureInfo.InvariantCulture);
|
||||
var bounds = node["Bounds"];
|
||||
var isMaximizedText = bounds.GetAttribute("IsMaximized");
|
||||
if (!string.IsNullOrEmpty(isMaximizedText))
|
||||
isMaximized = bool.Parse(isMaximizedText);
|
||||
var isMinimizedText = bounds.GetAttribute("IsMinimized");
|
||||
if (!string.IsNullOrEmpty(isMinimizedText))
|
||||
isMinimized = bool.Parse(isMinimizedText);
|
||||
float x = float.Parse(bounds.GetAttribute("X"), CultureInfo.InvariantCulture);
|
||||
float y = float.Parse(bounds.GetAttribute("Y"), CultureInfo.InvariantCulture);
|
||||
float width = float.Parse(bounds.GetAttribute("Width"), CultureInfo.InvariantCulture);
|
||||
float height = float.Parse(bounds.GetAttribute("Height"), CultureInfo.InvariantCulture);
|
||||
return new Rectangle(x, y, width, height);
|
||||
}
|
||||
|
||||
private static void LoadWindow(Window win, ref Rectangle bounds, bool isMaximized, bool isMinimized)
|
||||
{
|
||||
var virtualDesktopBounds = Platform.VirtualDesktopBounds;
|
||||
var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location;
|
||||
var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight;
|
||||
|
||||
// Clamp position to match current desktop dimensions (if window was on desktop that is now inactive)
|
||||
if (bounds.X < virtualDesktopSafeLeftCorner.X || bounds.Y < virtualDesktopSafeLeftCorner.Y || bounds.X > virtualDesktopSafeRightCorner.X || bounds.Y > virtualDesktopSafeRightCorner.Y)
|
||||
bounds.Location = virtualDesktopSafeLeftCorner;
|
||||
|
||||
if (isMaximized)
|
||||
{
|
||||
if (win.IsMaximized)
|
||||
win.Restore();
|
||||
win.ClientPosition = bounds.Location;
|
||||
win.Maximize();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Mathf.Min(bounds.Size.X, bounds.Size.Y) >= 1)
|
||||
{
|
||||
win.ClientBounds = bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
win.ClientPosition = bounds.Location;
|
||||
}
|
||||
if (isMinimized)
|
||||
win.Minimize();
|
||||
}
|
||||
}
|
||||
|
||||
private class LayoutNameDialog : Dialog
|
||||
{
|
||||
private TextBox _textbox;
|
||||
@@ -609,13 +630,8 @@ namespace FlaxEditor.Modules
|
||||
if (MainWindow)
|
||||
{
|
||||
writer.WriteStartElement("MainWindow");
|
||||
writer.WriteAttributeString("IsMaximized", MainWindow.IsMaximized.ToString());
|
||||
|
||||
writer.WriteStartElement("Bounds");
|
||||
SaveBounds(writer, MainWindow);
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
// Master panel structure
|
||||
@@ -628,22 +644,13 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
var panel = masterPanel.FloatingPanels[i];
|
||||
var window = panel.Window;
|
||||
|
||||
if (window == null)
|
||||
{
|
||||
Editor.LogWarning("Floating panel has missing window");
|
||||
continue;
|
||||
}
|
||||
|
||||
writer.WriteStartElement("Float");
|
||||
|
||||
SavePanel(writer, panel);
|
||||
|
||||
writer.WriteStartElement("Bounds");
|
||||
SaveBounds(writer, window.Window);
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
writer.WriteEndElement();
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace FlaxEditor.Options
|
||||
/// <summary>
|
||||
/// Gets or sets the sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).
|
||||
/// </summary>
|
||||
[EditorDisplay("General"), EditorOrder(200), Tooltip("The sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).")]
|
||||
[EditorDisplay("General"), EditorOrder(200), ExpandGroups, Tooltip("The sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).")]
|
||||
public BuildAction[] BuildActions { get; set; } =
|
||||
{
|
||||
BuildAction.CSG,
|
||||
|
||||
@@ -244,11 +244,11 @@ namespace FlaxEditor.Options
|
||||
CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC),
|
||||
ProgressNormal = Color.FromBgra(0xFF0ad328),
|
||||
|
||||
Statusbar = new Style.StatusbarStyle()
|
||||
Statusbar = new Style.StatusbarStyle
|
||||
{
|
||||
PlayMode = Color.FromBgra(0xFF2F9135),
|
||||
Failed = Color.FromBgra(0xFF9C2424),
|
||||
Loading = Color.FromBgra(0xFF2D2D30)
|
||||
Loading = Color.FromBgra(0xFF2D2D30),
|
||||
},
|
||||
|
||||
// Fonts
|
||||
@@ -271,7 +271,7 @@ namespace FlaxEditor.Options
|
||||
Scale = Editor.Icons.Scale32,
|
||||
Scalar = Editor.Icons.Scalar32,
|
||||
|
||||
SharedTooltip = new Tooltip()
|
||||
SharedTooltip = new Tooltip(),
|
||||
};
|
||||
style.DragWindow = style.BackgroundSelected * 0.7f;
|
||||
|
||||
|
||||
@@ -19,25 +19,25 @@ namespace FlaxEditor.Progress.Handlers
|
||||
public ImportAssetsProgress()
|
||||
{
|
||||
var importing = Editor.Instance.ContentImporting;
|
||||
importing.ImportingQueueBegin += OnStart;
|
||||
importing.ImportingQueueEnd += OnEnd;
|
||||
importing.ImportingQueueBegin += () => FlaxEngine.Scripting.InvokeOnUpdate(OnStart);
|
||||
importing.ImportingQueueEnd += () => FlaxEngine.Scripting.InvokeOnUpdate(OnEnd);
|
||||
importing.ImportFileBegin += OnImportFileBegin;
|
||||
}
|
||||
|
||||
private void OnImportFileBegin(IFileEntryAction importFileEntry)
|
||||
{
|
||||
string info;
|
||||
if (importFileEntry is ImportFileEntry)
|
||||
_currentInfo = string.Format("Importing \'{0}\'", System.IO.Path.GetFileName(importFileEntry.SourceUrl));
|
||||
info = string.Format("Importing \'{0}\'", System.IO.Path.GetFileName(importFileEntry.SourceUrl));
|
||||
else
|
||||
_currentInfo = string.Format("Creating \'{0}\'", importFileEntry.SourceUrl);
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
private void UpdateProgress()
|
||||
{
|
||||
var importing = Editor.Instance.ContentImporting;
|
||||
var info = string.Format("{0} ({1}/{2})...", _currentInfo, importing.ImportBatchDone, importing.ImportBatchSize);
|
||||
OnUpdate(importing.ImportingProgress, info);
|
||||
info = string.Format("Creating \'{0}\'", importFileEntry.SourceUrl);
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() =>
|
||||
{
|
||||
_currentInfo = info;
|
||||
var importing = Editor.Instance.ContentImporting;
|
||||
var text = string.Format("{0} ({1}/{2})...", _currentInfo, importing.ImportBatchDone, importing.ImportBatchSize);
|
||||
OnUpdate(importing.ImportingProgress, text);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace FlaxEditor.Progress
|
||||
/// </summary>
|
||||
/// <param name="handler">The calling handler.</param>
|
||||
public delegate void ProgressDelegate(ProgressHandler handler);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Progress failed handler event delegate
|
||||
/// </summary>
|
||||
@@ -127,7 +127,7 @@ namespace FlaxEditor.Progress
|
||||
{
|
||||
if (!_isActive)
|
||||
throw new InvalidOperationException("Already ended.");
|
||||
|
||||
|
||||
_isActive = false;
|
||||
_progress = 0;
|
||||
_infoText = string.Empty;
|
||||
|
||||
@@ -286,81 +286,17 @@ namespace VisualStudio
|
||||
return "Visual Studio open timout";
|
||||
}
|
||||
|
||||
static ComPtr<EnvDTE::ProjectItem> FindItem(const ComPtr<EnvDTE::ProjectItems>& projectItems, BSTR filePath)
|
||||
{
|
||||
long count;
|
||||
projectItems->get_Count(&count);
|
||||
if (count == 0)
|
||||
return nullptr;
|
||||
|
||||
for (long i = 1; i <= count; i++) // They are counting from [1..Count]
|
||||
{
|
||||
ComPtr<EnvDTE::ProjectItem> projectItem;
|
||||
projectItems->Item(_variant_t(i), &projectItem);
|
||||
if (!projectItem)
|
||||
continue;
|
||||
|
||||
short fileCount = 0;
|
||||
projectItem->get_FileCount(&fileCount);
|
||||
for (short fileIndex = 1; fileIndex <= fileCount; fileIndex++)
|
||||
{
|
||||
_bstr_t filename;
|
||||
projectItem->get_FileNames(fileIndex, filename.GetAddress());
|
||||
|
||||
if (filename.GetBSTR() != nullptr && AreFilePathsEqual(filePath, filename))
|
||||
{
|
||||
return projectItem;
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr<EnvDTE::ProjectItems> childProjectItems;
|
||||
projectItem->get_ProjectItems(&childProjectItems);
|
||||
if (childProjectItems)
|
||||
{
|
||||
ComPtr<EnvDTE::ProjectItem> result = FindItem(childProjectItems, filePath);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static ComPtr<EnvDTE::ProjectItem> FindItem(const ComPtr<EnvDTE::_Solution>& solution, BSTR filePath)
|
||||
{
|
||||
static ComPtr<EnvDTE::ProjectItem> FindItem(const ComPtr<EnvDTE::_Solution>& solution, BSTR filePath)
|
||||
{
|
||||
HRESULT result;
|
||||
|
||||
ComPtr<EnvDTE::Projects> projects;
|
||||
result = solution->get_Projects(&projects);
|
||||
ComPtr<EnvDTE::ProjectItem> projectItem;
|
||||
result = solution->FindProjectItem(filePath, &projectItem);
|
||||
if (FAILED(result))
|
||||
return nullptr;
|
||||
|
||||
long projectsCount = 0;
|
||||
result = projects->get_Count(&projectsCount);
|
||||
if (FAILED(result))
|
||||
return nullptr;
|
||||
|
||||
for (long projectIndex = 1; projectIndex <= projectsCount; projectIndex++) // They are counting from [1..Count]
|
||||
{
|
||||
ComPtr<EnvDTE::Project> project;
|
||||
result = projects->Item(_variant_t(projectIndex), &project);
|
||||
if (FAILED(result) || !project)
|
||||
continue;
|
||||
|
||||
ComPtr<EnvDTE::ProjectItems> projectItems;
|
||||
result = project->get_ProjectItems(&projectItems);
|
||||
if (FAILED(result) || !projectItems)
|
||||
continue;
|
||||
|
||||
auto projectItem = FindItem(projectItems, filePath);
|
||||
if (projectItem)
|
||||
{
|
||||
return projectItem;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return projectItem;
|
||||
}
|
||||
|
||||
// Opens a file on a specific line in a running Visual Studio instance.
|
||||
//
|
||||
|
||||
@@ -170,7 +170,7 @@ bool ScriptsBuilder::IsSourceWorkspaceDirty()
|
||||
bool ScriptsBuilder::IsSourceDirtyFor(const TimeSpan& timeout)
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
return _lastSourceCodeEdited > (_lastCompileAction + timeout);
|
||||
return _lastSourceCodeEdited > _lastCompileAction && DateTime::Now() > _lastSourceCodeEdited + timeout;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::IsCompiling()
|
||||
@@ -266,7 +266,7 @@ bool ScriptsBuilder::RunBuildTool(const StringView& args, const StringView& work
|
||||
|
||||
bool ScriptsBuilder::GenerateProject(const StringView& customArgs)
|
||||
{
|
||||
String args(TEXT("-log -genproject "));
|
||||
String args(TEXT("-log -mutex -genproject "));
|
||||
args += customArgs;
|
||||
_wasProjectStructureChanged = false;
|
||||
return RunBuildTool(args);
|
||||
@@ -669,7 +669,7 @@ void ScriptsBuilderService::Update()
|
||||
}
|
||||
|
||||
// Check if compile code (if has been edited)
|
||||
const TimeSpan timeToCallCompileIfDirty = TimeSpan::FromMilliseconds(50);
|
||||
const TimeSpan timeToCallCompileIfDirty = TimeSpan::FromMilliseconds(150);
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (ScriptsBuilder::IsSourceDirtyFor(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused())
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="timeout">Time to use for checking.</param>
|
||||
/// <returns>True if source code is dirty, otherwise false.</returns>
|
||||
static bool IsSourceDirtyFor(const TimeSpan& timeout);
|
||||
API_FUNCTION() static bool IsSourceDirtyFor(const TimeSpan& timeout);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if scripts are being now compiled/reloaded.
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
|
||||
public class Sample : SurfaceNode
|
||||
{
|
||||
private AssetSelect _assetSelect;
|
||||
private Box _assetBox;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Sample(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
@@ -54,16 +57,42 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
base.OnSurfaceLoaded(action);
|
||||
|
||||
if (Surface != null)
|
||||
{
|
||||
_assetSelect = GetChild<AssetSelect>();
|
||||
if (TryGetBox(8, out var box))
|
||||
{
|
||||
_assetBox = box;
|
||||
_assetSelect.Visible = !_assetBox.HasAnyConnection;
|
||||
}
|
||||
UpdateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTitle()
|
||||
{
|
||||
var asset = Editor.Instance.ContentDatabase.Find((Guid)Values[0]);
|
||||
Title = asset?.ShortName ?? "Animation";
|
||||
if (_assetBox != null)
|
||||
Title = _assetBox.HasAnyConnection || asset == null ? "Animation" : asset.ShortName;
|
||||
else
|
||||
Title = asset?.ShortName ?? "Animation";
|
||||
|
||||
var style = Style.Current;
|
||||
Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ConnectionTick(Box box)
|
||||
{
|
||||
base.ConnectionTick(box);
|
||||
|
||||
if (_assetBox == null)
|
||||
return;
|
||||
if (box.ID != _assetBox.ID)
|
||||
return;
|
||||
|
||||
_assetSelect.Visible = !box.HasAnyConnection;
|
||||
UpdateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -305,7 +334,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Input(0, "Speed", true, typeof(float), 5, 1),
|
||||
NodeElementArchetype.Factory.Input(1, "Loop", true, typeof(bool), 6, 2),
|
||||
NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 7, 3),
|
||||
NodeElementArchetype.Factory.Asset(0, Surface.Constants.LayoutOffsetY * 3, 0, typeof(FlaxEngine.Animation)),
|
||||
NodeElementArchetype.Factory.Input(3, "Animation Asset", true, typeof(FlaxEngine.Animation), 8),
|
||||
NodeElementArchetype.Factory.Asset(0, Surface.Constants.LayoutOffsetY * 4, 0, typeof(FlaxEngine.Animation)),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Size = new Float2(200, 100),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
0.0f,
|
||||
0.5f,
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
|
||||
@@ -510,7 +510,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
for (var i = 0; i < elements.Length; i++)
|
||||
{
|
||||
if(elements[i].Type != NodeElementType.Output)
|
||||
if (elements[i].Type != NodeElementType.Output)
|
||||
continue;
|
||||
|
||||
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint))
|
||||
@@ -533,7 +533,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
for (var i = 0; i < elements.Length; i++)
|
||||
{
|
||||
if(elements[i].Type != NodeElementType.Input)
|
||||
if (elements[i].Type != NodeElementType.Input)
|
||||
continue;
|
||||
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint))
|
||||
return true;
|
||||
@@ -725,7 +725,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool UseNormalMaps => false;
|
||||
|
||||
|
||||
internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||
{
|
||||
if (inputType == ScriptType.Object)
|
||||
@@ -743,7 +743,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
for (var i = 0; i < elements.Length; i++)
|
||||
{
|
||||
if(elements[i].Type != NodeElementType.Output)
|
||||
if (elements[i].Type != NodeElementType.Output)
|
||||
continue;
|
||||
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint))
|
||||
return true;
|
||||
@@ -765,7 +765,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
for (var i = 0; i < elements.Length; i++)
|
||||
{
|
||||
if(elements[i].Type != NodeElementType.Input)
|
||||
if (elements[i].Type != NodeElementType.Input)
|
||||
continue;
|
||||
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint))
|
||||
return true;
|
||||
@@ -789,7 +789,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool UseNormalMaps => false;
|
||||
|
||||
|
||||
internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||
{
|
||||
if (inputType == ScriptType.Object)
|
||||
@@ -987,7 +987,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_combobox.Width = Width - 50;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||
{
|
||||
return inputType == ScriptType.Void;
|
||||
@@ -997,7 +997,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
if (outputType == ScriptType.Void)
|
||||
return true;
|
||||
|
||||
|
||||
SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]);
|
||||
ScriptType type = parameter?.Type ?? ScriptType.Null;
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace FlaxEditor.Surface
|
||||
|
||||
if (_connectionInstigator is Archetypes.Tools.RerouteNode)
|
||||
{
|
||||
if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true})
|
||||
if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true })
|
||||
{
|
||||
actualStartPos = endPos;
|
||||
actualEndPos = startPos;
|
||||
|
||||
@@ -150,6 +150,12 @@ namespace FlaxEditor.Tools.Terrain
|
||||
return;
|
||||
}
|
||||
|
||||
// Increase or decrease brush size with scroll
|
||||
if (Input.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
Mode.CurrentBrush.Size += dt * Mode.CurrentBrush.Size * Input.Mouse.ScrollDelta * 5f;
|
||||
}
|
||||
|
||||
// Check if no terrain is selected
|
||||
var terrain = SelectedTerrain;
|
||||
if (!terrain)
|
||||
|
||||
@@ -158,6 +158,12 @@ namespace FlaxEditor.Tools.Terrain
|
||||
return;
|
||||
}
|
||||
|
||||
// Increase or decrease brush size with scroll
|
||||
if (Input.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
Mode.CurrentBrush.Size += dt * Mode.CurrentBrush.Size * Input.Mouse.ScrollDelta * 5f;
|
||||
}
|
||||
|
||||
// Check if selected terrain was changed during painting
|
||||
if (terrain != _paintTerrain && IsPainting)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,16 @@ namespace FlaxEditor.Actions
|
||||
_scriptTypeName = script.TypeName;
|
||||
_prefabId = script.PrefabID;
|
||||
_prefabObjectId = script.PrefabObjectID;
|
||||
_scriptData = FlaxEngine.Json.JsonSerializer.Serialize(script);
|
||||
try
|
||||
{
|
||||
_scriptData = FlaxEngine.Json.JsonSerializer.Serialize(script);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_scriptData = null;
|
||||
Debug.LogError("Failed to serialize script data for Undo due to exception");
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
_parentId = script.Actor.ID;
|
||||
_orderInParent = script.OrderInParent;
|
||||
_enabled = script.Enabled;
|
||||
@@ -66,6 +75,11 @@ namespace FlaxEditor.Actions
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
public int GetOrderInParent()
|
||||
{
|
||||
return _orderInParent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new added script undo action.
|
||||
/// </summary>
|
||||
@@ -175,6 +189,7 @@ namespace FlaxEditor.Actions
|
||||
script.Parent = parentActor;
|
||||
if (_orderInParent != -1)
|
||||
script.OrderInParent = _orderInParent;
|
||||
_orderInParent = script.OrderInParent; // Ensure order is correct for script that want to use it later
|
||||
if (_prefabObjectId != Guid.Empty)
|
||||
SceneObject.Internal_LinkPrefab(Object.GetUnmanagedPtr(script), ref _prefabId, ref _prefabObjectId);
|
||||
Editor.Instance.Scene.MarkSceneEdited(parentActor.Scene);
|
||||
|
||||
@@ -394,8 +394,12 @@ bool EditorUtilities::UpdateExeIcon(const String& path, const TextureData& icon)
|
||||
// - icon/cursor/etc data
|
||||
|
||||
std::fstream stream;
|
||||
#if PLATFORM_WINDOWS
|
||||
stream.open(path.Get(), std::ios::in | std::ios::out | std::ios::binary);
|
||||
#else
|
||||
StringAsANSI<> pathAnsi(path.Get());
|
||||
stream.open(pathAnsi.Get(), std::ios::in | std::ios::out | std::ios::binary);
|
||||
#endif
|
||||
if (!stream.is_open())
|
||||
{
|
||||
LOG(Warning, "Cannot open file");
|
||||
|
||||
@@ -73,6 +73,7 @@ Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
outputColor.R = color.red / 256;
|
||||
outputColor.G = color.green / 256;
|
||||
outputColor.B = color.blue / 256;
|
||||
outputColor.A = 255;
|
||||
return outputColor;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ namespace FlaxEditor.Viewport
|
||||
Gizmos[i].Update(deltaTime);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public EditorViewport Viewport => this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public GizmosCollection Gizmos { get; }
|
||||
|
||||
@@ -593,12 +593,14 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
ref var vv = ref v.Options[j];
|
||||
var button = childMenu.AddButton(vv.Name);
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Tag = vv.Mode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var button = debugView.AddButton(v.Name);
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Tag = v.Mode;
|
||||
}
|
||||
}
|
||||
@@ -1119,7 +1121,12 @@ namespace FlaxEditor.Viewport
|
||||
var win = (WindowRootControl)Root;
|
||||
|
||||
// Get current mouse position in the view
|
||||
_viewMousePos = PointFromWindow(win.MousePosition);
|
||||
{
|
||||
// When the window is not focused, the position in window does not return sane values
|
||||
Float2 pos = PointFromWindow(win.MousePosition);
|
||||
if (!float.IsInfinity(pos.LengthSquared))
|
||||
_viewMousePos = pos;
|
||||
}
|
||||
|
||||
// Update input
|
||||
var window = win.Window;
|
||||
@@ -1582,7 +1589,14 @@ namespace FlaxEditor.Viewport
|
||||
private void WidgetViewModeShowHideClicked(ContextMenuButton button)
|
||||
{
|
||||
if (button.Tag is ViewMode v)
|
||||
{
|
||||
Task.ViewMode = v;
|
||||
var cm = button.ParentContextMenu;
|
||||
WidgetViewModeShowHide(cm);
|
||||
var mainCM = ViewWidgetButtonMenu.GetChildMenu("Debug View").ContextMenu;
|
||||
if (mainCM != null && cm != mainCM)
|
||||
WidgetViewModeShowHide(mainCM);
|
||||
}
|
||||
}
|
||||
|
||||
private void WidgetViewModeShowHide(Control cm)
|
||||
@@ -1594,7 +1608,7 @@ namespace FlaxEditor.Viewport
|
||||
foreach (var e in ccm.Items)
|
||||
{
|
||||
if (e is ContextMenuButton b && b.Tag is ViewMode v)
|
||||
b.Icon = Task.View.Mode == v ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
b.Icon = Task.ViewMode == v ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -306,6 +306,8 @@ namespace FlaxEditor.Viewport
|
||||
var orient = ViewOrientation;
|
||||
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public EditorViewport Viewport => this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public GizmosCollection Gizmos { get; }
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
case DrawModes.Fill:
|
||||
clipsInView = 1.0f;
|
||||
clipWidth = width;
|
||||
samplesPerIndex = (uint)(samplesPerChannel / width);
|
||||
samplesPerIndex = (uint)(samplesPerChannel / width) * info.NumChannels;
|
||||
break;
|
||||
case DrawModes.Single:
|
||||
clipsInView = Mathf.Min(clipsInView, 1.0f);
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace FlaxEditor
|
||||
if (_highlightMaterial == null
|
||||
|| (_highlights.Count == 0 && _highlightTriangles.Count == 0)
|
||||
|| renderContext.View.Pass == DrawPass.Depth
|
||||
)
|
||||
)
|
||||
return;
|
||||
Profiler.BeginEvent("ViewportDebugDrawData.OnDraw");
|
||||
|
||||
|
||||
@@ -388,14 +388,16 @@ namespace FlaxEditor.Windows.Assets
|
||||
protected override void OnShow()
|
||||
{
|
||||
// Check if has no asset (but has item linked)
|
||||
if (_asset == null && _item != null)
|
||||
var item = _item;
|
||||
if (_asset == null && item != null)
|
||||
{
|
||||
// Load asset
|
||||
_asset = LoadAsset();
|
||||
if (_asset == null)
|
||||
{
|
||||
Editor.LogError(string.Format("Cannot load asset \'{0}\' ({1})", _item.Path, typeof(T)));
|
||||
Editor.LogError(string.Format("Cannot load asset \'{0}\' ({1})", item.Path, typeof(T)));
|
||||
Close();
|
||||
Editor.ContentDatabase.RefreshFolder(item, false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -335,6 +335,22 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (base.OnKeyDown(key))
|
||||
return true;
|
||||
|
||||
if (key == KeyboardKeys.Spacebar)
|
||||
{
|
||||
if (_previewSource?.State == AudioSource.States.Playing)
|
||||
OnPause();
|
||||
else
|
||||
OnPlay();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool UseLayoutData => true;
|
||||
|
||||
|
||||
@@ -521,8 +521,11 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <inheritdoc />
|
||||
protected override void OnClose()
|
||||
{
|
||||
// Discard unsaved changes
|
||||
_properties.DiscardChanges();
|
||||
if (Asset)
|
||||
{
|
||||
// Discard unsaved changes
|
||||
_properties.DiscardChanges();
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
_undo.Clear();
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
OnPasteAction(pasteAction);
|
||||
}
|
||||
|
||||
|
||||
// Scroll to new selected node
|
||||
ScrollToSelectedNode();
|
||||
}
|
||||
@@ -183,7 +183,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
OnPasteAction(pasteAction);
|
||||
}
|
||||
|
||||
|
||||
// Scroll to new selected node
|
||||
ScrollToSelectedNode();
|
||||
}
|
||||
@@ -334,7 +334,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
}, action2.ActionString);
|
||||
action.Do();
|
||||
Undo.AddAction(action);
|
||||
|
||||
|
||||
_treePanel.PerformLayout();
|
||||
_treePanel.PerformLayout();
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
InputActions.Add(options => options.Rename, Rename);
|
||||
InputActions.Add(options => options.FocusSelection, _viewport.FocusSelection);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables vertical and horizontal scrolling on the tree panel.
|
||||
/// </summary>
|
||||
@@ -257,7 +257,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
if (base.OnMouseUp(location, button))
|
||||
return true;
|
||||
|
||||
|
||||
if (button == MouseButton.Right && _treePanel.ContainsPoint(ref location))
|
||||
{
|
||||
_tree.Deselect();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Scripting;
|
||||
@@ -249,6 +250,10 @@ namespace FlaxEditor.Windows
|
||||
});
|
||||
}
|
||||
|
||||
// Remove any leftover separator
|
||||
if (cm.ItemsContainer.Children.LastOrDefault() is ContextMenuSeparator)
|
||||
cm.ItemsContainer.Children.Last().Dispose();
|
||||
|
||||
// Show it
|
||||
cm.Show(this, location);
|
||||
}
|
||||
|
||||
@@ -629,8 +629,9 @@ namespace FlaxEditor.Windows
|
||||
if (items.Count == 0)
|
||||
return;
|
||||
|
||||
// TODO: remove items that depend on different items in the list: use wants to remove `folderA` and `folderA/asset.x`, we should just remove `folderA`
|
||||
// Sort items to remove files first, then folders
|
||||
var toDelete = new List<ContentItem>(items);
|
||||
toDelete.Sort((a, b) => a.IsFolder ? 1 : b.IsFolder ? -1 : a.Compare(b));
|
||||
|
||||
string msg = toDelete.Count == 1
|
||||
? string.Format("Are you sure to delete \'{0}\'?\nThis action cannot be undone. Files will be deleted permanently.", items[0].Path)
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Networking;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
@@ -37,11 +38,14 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
private readonly SingleChart _dataSentChart;
|
||||
private readonly SingleChart _dataReceivedChart;
|
||||
private readonly SingleChart _dataSentRateChart;
|
||||
private readonly SingleChart _dataReceivedRateChart;
|
||||
private readonly Table _tableRpc;
|
||||
private readonly Table _tableRep;
|
||||
private SamplesBuffer<ProfilingTools.NetworkEventStat[]> _events;
|
||||
private List<Row> _tableRowsCache;
|
||||
private FlaxEngine.Networking.NetworkDriverStats _prevStats;
|
||||
private SamplesBuffer<ProfilingTools.NetworkEventStat[]> _events;
|
||||
private NetworkDriverStats _prevStats;
|
||||
private List<NetworkDriverStats> _stats;
|
||||
|
||||
public Network()
|
||||
: base("Network")
|
||||
@@ -76,6 +80,20 @@ namespace FlaxEditor.Windows.Profiler
|
||||
Parent = layout,
|
||||
};
|
||||
_dataReceivedChart.SelectedSampleChanged += OnSelectedSampleChanged;
|
||||
_dataSentRateChart = new SingleChart
|
||||
{
|
||||
Title = "Data Sent Rate",
|
||||
FormatSample = FormatSampleBytesRate,
|
||||
Parent = layout,
|
||||
};
|
||||
_dataSentRateChart.SelectedSampleChanged += OnSelectedSampleChanged;
|
||||
_dataReceivedRateChart = new SingleChart
|
||||
{
|
||||
Title = "Data Received Rate",
|
||||
FormatSample = FormatSampleBytesRate,
|
||||
Parent = layout,
|
||||
};
|
||||
_dataReceivedRateChart.SelectedSampleChanged += OnSelectedSampleChanged;
|
||||
|
||||
// Tables
|
||||
_tableRpc = InitTable(layout, "RPC Name");
|
||||
@@ -87,24 +105,52 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
_dataSentChart.Clear();
|
||||
_dataReceivedChart.Clear();
|
||||
_dataSentRateChart.Clear();
|
||||
_dataReceivedRateChart.Clear();
|
||||
_events?.Clear();
|
||||
_stats?.Clear();
|
||||
_prevStats = new NetworkDriverStats();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(ref SharedUpdateData sharedData)
|
||||
{
|
||||
// Gather peer stats
|
||||
var peers = FlaxEngine.Networking.NetworkPeer.Peers;
|
||||
var stats = new FlaxEngine.Networking.NetworkDriverStats();
|
||||
var peers = NetworkPeer.Peers;
|
||||
var thisStats = new NetworkDriverStats();
|
||||
thisStats.RTT = Time.UnscaledGameTime; // Store sample time in RTT
|
||||
foreach (var peer in peers)
|
||||
{
|
||||
var peerStats = peer.NetworkDriver.GetStats();
|
||||
stats.TotalDataSent += peerStats.TotalDataSent;
|
||||
stats.TotalDataReceived += peerStats.TotalDataReceived;
|
||||
thisStats.TotalDataSent += peerStats.TotalDataSent;
|
||||
thisStats.TotalDataReceived += peerStats.TotalDataReceived;
|
||||
}
|
||||
_dataSentChart.AddSample(Mathf.Max((long)stats.TotalDataSent - (long)_prevStats.TotalDataSent, 0));
|
||||
_dataReceivedChart.AddSample(Mathf.Max((long)stats.TotalDataReceived - (long)_prevStats.TotalDataReceived, 0));
|
||||
_prevStats = stats;
|
||||
var stats = thisStats;
|
||||
stats.TotalDataSent = (uint)Mathf.Max((long)thisStats.TotalDataSent - (long)_prevStats.TotalDataSent, 0);
|
||||
stats.TotalDataReceived = (uint)Mathf.Max((long)thisStats.TotalDataReceived - (long)_prevStats.TotalDataReceived, 0);
|
||||
_dataSentChart.AddSample(stats.TotalDataSent);
|
||||
_dataReceivedChart.AddSample(stats.TotalDataReceived);
|
||||
_prevStats = thisStats;
|
||||
if (_stats == null)
|
||||
_stats = new List<NetworkDriverStats>();
|
||||
_stats.Add(stats);
|
||||
|
||||
// Remove all stats older than 1 second
|
||||
while (_stats.Count > 0 && thisStats.RTT - _stats[0].RTT >= 1.0f)
|
||||
_stats.RemoveAt(0);
|
||||
|
||||
// Calculate average data rates (from last second)
|
||||
var avgStats = new NetworkDriverStats();
|
||||
foreach (var e in _stats)
|
||||
{
|
||||
avgStats.TotalDataSent += e.TotalDataSent;
|
||||
avgStats.TotalDataReceived += e.TotalDataReceived;
|
||||
}
|
||||
avgStats.TotalDataSent /= (uint)_stats.Count;
|
||||
avgStats.TotalDataReceived /= (uint)_stats.Count;
|
||||
_dataSentRateChart.AddSample(avgStats.TotalDataSent);
|
||||
_dataReceivedRateChart.AddSample(avgStats.TotalDataReceived);
|
||||
|
||||
|
||||
// Gather network events
|
||||
var events = ProfilingTools.EventsNetwork;
|
||||
@@ -118,6 +164,8 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
_dataSentChart.SelectedSampleIndex = selectedFrame;
|
||||
_dataReceivedChart.SelectedSampleIndex = selectedFrame;
|
||||
_dataSentRateChart.SelectedSampleIndex = selectedFrame;
|
||||
_dataReceivedRateChart.SelectedSampleIndex = selectedFrame;
|
||||
|
||||
// Update events tables
|
||||
if (_events != null)
|
||||
@@ -128,7 +176,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
_tableRep.IsLayoutLocked = true;
|
||||
RecycleTableRows(_tableRpc, _tableRowsCache);
|
||||
RecycleTableRows(_tableRep, _tableRowsCache);
|
||||
|
||||
|
||||
var events = _events.Get(selectedFrame);
|
||||
var rowCount = Int2.Zero;
|
||||
if (events != null && events.Length != 0)
|
||||
@@ -186,7 +234,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
_tableRep.Visible = rowCount.Y != 0;
|
||||
_tableRpc.Children.Sort(SortRows);
|
||||
_tableRep.Children.Sort(SortRows);
|
||||
|
||||
|
||||
_tableRpc.UnlockChildrenRecursive();
|
||||
_tableRpc.PerformLayout();
|
||||
_tableRep.UnlockChildrenRecursive();
|
||||
@@ -257,6 +305,11 @@ namespace FlaxEditor.Windows.Profiler
|
||||
return Utilities.Utils.FormatBytesCount((ulong)v);
|
||||
}
|
||||
|
||||
private static string FormatSampleBytesRate(float v)
|
||||
{
|
||||
return Utilities.Utils.FormatBytesCount((ulong)v) + "/s";
|
||||
}
|
||||
|
||||
private static string FormatCellBytes(object x)
|
||||
{
|
||||
return Utilities.Utils.FormatBytesCount((int)x);
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
/// <returns>The sample value</returns>
|
||||
public T Get(int index)
|
||||
{
|
||||
if (index >= _data.Length || _data.Length == 0)
|
||||
if (_count == 0 || index >= _data.Length || _data.Length == 0)
|
||||
return default;
|
||||
return index == -1 ? _data[_count - 1] : _data[index];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "AnimGraph.h"
|
||||
#include "Engine/Core/Types/VariantValueCast.h"
|
||||
#include "Engine/Content/Assets/Animation.h"
|
||||
#include "Engine/Content/Assets/SkeletonMask.h"
|
||||
#include "Engine/Content/Assets/AnimationGraphFunction.h"
|
||||
@@ -512,18 +513,6 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const
|
||||
transitionIndex++;
|
||||
continue;
|
||||
}
|
||||
const bool useDefaultRule = EnumHasAnyFlags(transition.Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule);
|
||||
if (transition.RuleGraph && !useDefaultRule)
|
||||
{
|
||||
// Execute transition rule
|
||||
auto rootNode = transition.RuleGraph->GetRootNode();
|
||||
ASSERT(rootNode);
|
||||
if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0]))
|
||||
{
|
||||
transitionIndex++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate source state transition data (position, length, etc.)
|
||||
const Value sourceStatePtr = SampleState(stateMachineBucket.CurrentState);
|
||||
@@ -543,6 +532,19 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const
|
||||
transitionData.Length = ZeroTolerance;
|
||||
}
|
||||
|
||||
const bool useDefaultRule = EnumHasAnyFlags(transition.Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule);
|
||||
if (transition.RuleGraph && !useDefaultRule)
|
||||
{
|
||||
// Execute transition rule
|
||||
auto rootNode = transition.RuleGraph->GetRootNode();
|
||||
ASSERT(rootNode);
|
||||
if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0]))
|
||||
{
|
||||
transitionIndex++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if can trigger the transition
|
||||
bool canEnter = false;
|
||||
if (useDefaultRule)
|
||||
@@ -749,9 +751,16 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Animation
|
||||
case 2:
|
||||
{
|
||||
const auto anim = node->Assets[0].As<Animation>();
|
||||
auto anim = node->Assets[0].As<Animation>();
|
||||
auto& bucket = context.Data->State[node->BucketIndex].Animation;
|
||||
|
||||
// Override animation when animation reference box is connected
|
||||
auto animationAssetBox = node->TryGetBox(8);
|
||||
if (animationAssetBox && animationAssetBox->HasConnection())
|
||||
{
|
||||
anim = TVariantValueCast<Animation*>::Cast(tryGetValue(animationAssetBox, Value::Null));
|
||||
}
|
||||
|
||||
switch (box->ID)
|
||||
{
|
||||
// Animation
|
||||
|
||||
@@ -187,7 +187,6 @@ float AudioSource::GetTime() const
|
||||
return 0.0f;
|
||||
|
||||
float time = AudioBackend::Source::GetCurrentBufferTime(this);
|
||||
ASSERT(time >= 0.0f && time <= Clip->GetLength());
|
||||
|
||||
if (UseStreaming())
|
||||
{
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
int alError = alGetError(); \
|
||||
if (alError != 0) \
|
||||
{ \
|
||||
LOG(Error, "OpenAL method {0} failed with error 0x{1:X} (at line {2})", TEXT(#method), alError, __LINE__ - 1); \
|
||||
const Char* errorStr = GetOpenALErrorString(alError); \
|
||||
LOG(Error, "OpenAL method {0} failed with error 0x{1:X}:{2} (at line {3})", TEXT(#method), alError, errorStr, __LINE__ - 1); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
@@ -290,6 +291,28 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Char* GetOpenALErrorString(int error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case AL_NO_ERROR:
|
||||
return TEXT("AL_NO_ERROR");
|
||||
case AL_INVALID_NAME:
|
||||
return TEXT("AL_INVALID_NAME");
|
||||
case AL_INVALID_ENUM:
|
||||
return TEXT("AL_INVALID_ENUM");
|
||||
case AL_INVALID_VALUE:
|
||||
return TEXT("AL_INVALID_VALUE");
|
||||
case AL_INVALID_OPERATION:
|
||||
return TEXT("AL_INVALID_OPERATION");
|
||||
case AL_OUT_OF_MEMORY:
|
||||
return TEXT("AL_OUT_OF_MEMORY");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TEXT("???");
|
||||
}
|
||||
|
||||
void AudioBackendOAL::Listener_OnAdd(AudioListener* listener)
|
||||
{
|
||||
#if ALC_MULTIPLE_LISTENERS
|
||||
@@ -838,7 +861,11 @@ bool AudioBackendOAL::Base_Init()
|
||||
// Init
|
||||
Base_SetDopplerFactor(AudioSettings::Get()->DopplerFactor);
|
||||
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); // Default attenuation model
|
||||
ALC::RebuildContexts(true);
|
||||
int32 clampedIndex = Math::Clamp(activeDeviceIndex, -1, Audio::Devices.Count() - 1);
|
||||
if (clampedIndex == Audio::GetActiveDeviceIndex())
|
||||
{
|
||||
ALC::RebuildContexts(true);
|
||||
}
|
||||
Audio::SetActiveDeviceIndex(activeDeviceIndex);
|
||||
#ifdef AL_SOFT_source_spatialize
|
||||
if (ALC::IsExtensionSupported("AL_SOFT_source_spatialize"))
|
||||
|
||||
@@ -27,7 +27,15 @@
|
||||
#define MAX_INPUT_CHANNELS 2
|
||||
#define MAX_OUTPUT_CHANNELS 8
|
||||
#define MAX_CHANNELS_MATRIX_SIZE (MAX_INPUT_CHANNELS*MAX_OUTPUT_CHANNELS)
|
||||
|
||||
#if ENABLE_ASSERTION
|
||||
#define XAUDIO2_CHECK_ERROR(method) \
|
||||
if (hr != 0) \
|
||||
{ \
|
||||
LOG(Error, "XAudio2 method {0} failed with error 0x{1:X} (at line {2})", TEXT(#method), (uint32)hr, __LINE__ - 1); \
|
||||
}
|
||||
#else
|
||||
#define XAUDIO2_CHECK_ERROR(method)
|
||||
#endif
|
||||
#define FLAX_COORD_SCALE 0.01f // units are meters
|
||||
#define FLAX_DST_TO_XAUDIO(x) x * FLAX_COORD_SCALE
|
||||
#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * FLAX_COORD_SCALE, vec.Y * FLAX_COORD_SCALE, vec.Z * FLAX_COORD_SCALE)
|
||||
@@ -104,7 +112,9 @@ namespace XAudio2
|
||||
|
||||
COM_DECLSPEC_NOTHROW void STDMETHODCALLTYPE OnVoiceError(THIS_ void* pBufferContext, HRESULT Error) override
|
||||
{
|
||||
#if ENABLE_ASSERTION
|
||||
LOG(Warning, "IXAudio2VoiceCallback::OnVoiceError! Error: 0x{0:x}", Error);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -121,7 +131,8 @@ namespace XAudio2
|
||||
XAUDIO2_SEND_DESCRIPTOR Destination;
|
||||
float Pitch;
|
||||
float Pan;
|
||||
float StartTime;
|
||||
float StartTimeForQueueBuffer;
|
||||
float LastBufferStartTime;
|
||||
float DopplerFactor;
|
||||
uint64 LastBufferStartSamplesPlayed;
|
||||
int32 BuffersProcessed;
|
||||
@@ -145,7 +156,8 @@ namespace XAudio2
|
||||
Destination.pOutputVoice = nullptr;
|
||||
Pitch = 1.0f;
|
||||
Pan = 0.0f;
|
||||
StartTime = 0.0f;
|
||||
StartTimeForQueueBuffer = 0.0f;
|
||||
LastBufferStartTime = 0.0f;
|
||||
IsDirty = false;
|
||||
Is3D = false;
|
||||
IsPlaying = false;
|
||||
@@ -255,18 +267,18 @@ namespace XAudio2
|
||||
buffer.pAudioData = aBuffer->Data.Get();
|
||||
buffer.AudioBytes = aBuffer->Data.Count();
|
||||
|
||||
if (aSource->StartTime > ZeroTolerance)
|
||||
if (aSource->StartTimeForQueueBuffer > ZeroTolerance)
|
||||
{
|
||||
buffer.PlayBegin = (UINT32)(aSource->StartTime * (aBuffer->Info.SampleRate * aBuffer->Info.NumChannels));
|
||||
buffer.PlayLength = aBuffer->Info.NumSamples / aBuffer->Info.NumChannels - buffer.PlayBegin;
|
||||
aSource->StartTime = 0;
|
||||
// Offset start position when playing buffer with a custom time offset
|
||||
const uint32 bytesPerSample = aBuffer->Info.BitDepth / 8 * aBuffer->Info.NumChannels;
|
||||
buffer.PlayBegin = (UINT32)(aSource->StartTimeForQueueBuffer * aBuffer->Info.SampleRate);
|
||||
buffer.PlayLength = (buffer.AudioBytes / bytesPerSample) - buffer.PlayBegin;
|
||||
aSource->LastBufferStartTime = aSource->StartTimeForQueueBuffer;
|
||||
aSource->StartTimeForQueueBuffer = 0;
|
||||
}
|
||||
|
||||
const HRESULT hr = aSource->Voice->SubmitSourceBuffer(&buffer);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG(Warning, "XAudio2: Failed to submit source buffer (error: 0x{0:x})", hr);
|
||||
}
|
||||
XAUDIO2_CHECK_ERROR(SubmitSourceBuffer);
|
||||
}
|
||||
|
||||
void VoiceCallback::OnBufferEnd(void* pBufferContext)
|
||||
@@ -375,7 +387,7 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source)
|
||||
const auto& header = clip->AudioHeader;
|
||||
auto& format = aSource->Format;
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
format.nChannels = source->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write)
|
||||
format.nChannels = clip->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write)
|
||||
format.nSamplesPerSec = header.Info.SampleRate;
|
||||
format.wBitsPerSample = header.Info.BitDepth;
|
||||
format.nBlockAlign = (WORD)(format.nChannels * (format.wBitsPerSample / 8));
|
||||
@@ -391,12 +403,10 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source)
|
||||
1,
|
||||
&aSource->Destination
|
||||
};
|
||||
const HRESULT hr = XAudio2::Instance->CreateSourceVoice(&aSource->Voice, &aSource->Format, 0, 2.0f, &aSource->Callback, &sendList);
|
||||
HRESULT hr = XAudio2::Instance->CreateSourceVoice(&aSource->Voice, &aSource->Format, 0, 2.0f, &aSource->Callback, &sendList);
|
||||
XAUDIO2_CHECK_ERROR(CreateSourceVoice);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG(Error, "Failed to create XAudio2 voice. Error: 0x{0:x}", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare source state
|
||||
aSource->Callback.Source = source;
|
||||
@@ -410,7 +420,8 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source)
|
||||
aSource->DopplerFactor = source->GetDopplerFactor();
|
||||
aSource->UpdateTransform(source);
|
||||
aSource->UpdateVelocity(source);
|
||||
aSource->Voice->SetVolume(source->GetVolume());
|
||||
hr = aSource->Voice->SetVolume(source->GetVolume());
|
||||
XAUDIO2_CHECK_ERROR(SetVolume);
|
||||
|
||||
// 0 is invalid ID so shift them
|
||||
sourceID++;
|
||||
@@ -451,7 +462,8 @@ void AudioBackendXAudio2::Source_VolumeChanged(AudioSource* source)
|
||||
auto aSource = XAudio2::GetSource(source);
|
||||
if (aSource && aSource->Voice)
|
||||
{
|
||||
aSource->Voice->SetVolume(source->GetVolume());
|
||||
const HRESULT hr = aSource->Voice->SetVolume(source->GetVolume());
|
||||
XAUDIO2_CHECK_ERROR(SetVolume);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,12 +506,18 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
|
||||
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1];
|
||||
XAudio2::Locker.Unlock();
|
||||
|
||||
HRESULT hr;
|
||||
const bool isPlaying = source->IsActuallyPlayingSth();
|
||||
if (isPlaying)
|
||||
aSource->Voice->Stop();
|
||||
{
|
||||
hr = aSource->Voice->Stop();
|
||||
XAUDIO2_CHECK_ERROR(Stop);
|
||||
}
|
||||
|
||||
aSource->Voice->FlushSourceBuffers();
|
||||
hr = aSource->Voice->FlushSourceBuffers();
|
||||
XAUDIO2_CHECK_ERROR(FlushSourceBuffers);
|
||||
aSource->LastBufferStartSamplesPlayed = 0;
|
||||
aSource->LastBufferStartTime = 0;
|
||||
aSource->BuffersProcessed = 0;
|
||||
|
||||
XAUDIO2_BUFFER buffer = { 0 };
|
||||
@@ -512,12 +530,15 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
|
||||
const UINT32 totalSamples = aBuffer->Info.NumSamples / aBuffer->Info.NumChannels;
|
||||
buffer.PlayBegin = state.SamplesPlayed % totalSamples;
|
||||
buffer.PlayLength = totalSamples - buffer.PlayBegin;
|
||||
aSource->StartTime = 0;
|
||||
aSource->StartTimeForQueueBuffer = 0;
|
||||
|
||||
XAudio2::QueueBuffer(aSource, source, bufferId, buffer);
|
||||
|
||||
if (isPlaying)
|
||||
aSource->Voice->Start();
|
||||
{
|
||||
hr = aSource->Voice->Start();
|
||||
XAUDIO2_CHECK_ERROR(Start);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioBackendXAudio2::Source_SpatialSetupChanged(AudioSource* source)
|
||||
@@ -572,7 +593,8 @@ void AudioBackendXAudio2::Source_Play(AudioSource* source)
|
||||
if (aSource && aSource->Voice && !aSource->IsPlaying)
|
||||
{
|
||||
// Play
|
||||
aSource->Voice->Start();
|
||||
const HRESULT hr = aSource->Voice->Start();
|
||||
XAUDIO2_CHECK_ERROR(Start);
|
||||
aSource->IsPlaying = true;
|
||||
}
|
||||
}
|
||||
@@ -583,7 +605,8 @@ void AudioBackendXAudio2::Source_Pause(AudioSource* source)
|
||||
if (aSource && aSource->Voice && aSource->IsPlaying)
|
||||
{
|
||||
// Pause
|
||||
aSource->Voice->Stop();
|
||||
const HRESULT hr = aSource->Voice->Stop();
|
||||
XAUDIO2_CHECK_ERROR(Stop);
|
||||
aSource->IsPlaying = false;
|
||||
}
|
||||
}
|
||||
@@ -593,14 +616,18 @@ void AudioBackendXAudio2::Source_Stop(AudioSource* source)
|
||||
auto aSource = XAudio2::GetSource(source);
|
||||
if (aSource && aSource->Voice)
|
||||
{
|
||||
aSource->StartTime = 0.0f;
|
||||
aSource->StartTimeForQueueBuffer = 0.0f;
|
||||
aSource->LastBufferStartTime = 0.0f;
|
||||
|
||||
// Pause
|
||||
aSource->Voice->Stop();
|
||||
HRESULT hr = aSource->Voice->Stop();
|
||||
XAUDIO2_CHECK_ERROR(Stop);
|
||||
aSource->IsPlaying = false;
|
||||
|
||||
// Unset streaming buffers to rewind
|
||||
aSource->Voice->FlushSourceBuffers();
|
||||
hr = aSource->Voice->FlushSourceBuffers();
|
||||
XAUDIO2_CHECK_ERROR(FlushSourceBuffers);
|
||||
Platform::Sleep(10); // TODO: find a better way to handle case when VoiceCallback::OnBufferEnd is called after source was stopped thus BuffersProcessed != 0, probably via buffers contexts ptrs
|
||||
aSource->BuffersProcessed = 0;
|
||||
aSource->Callback.PeekSamples();
|
||||
}
|
||||
@@ -612,7 +639,7 @@ void AudioBackendXAudio2::Source_SetCurrentBufferTime(AudioSource* source, float
|
||||
if (aSource)
|
||||
{
|
||||
// Store start time so next buffer submitted will start from here (assumes audio is stopped)
|
||||
aSource->StartTime = value;
|
||||
aSource->StartTimeForQueueBuffer = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,8 +655,9 @@ float AudioBackendXAudio2::Source_GetCurrentBufferTime(const AudioSource* source
|
||||
aSource->Voice->GetState(&state);
|
||||
const uint32 numChannels = clipInfo.NumChannels;
|
||||
const uint32 totalSamples = clipInfo.NumSamples / numChannels;
|
||||
const uint32 sampleRate = clipInfo.SampleRate;// / clipInfo.NumChannels;
|
||||
state.SamplesPlayed -= aSource->LastBufferStartSamplesPlayed % totalSamples; // Offset by the last buffer start to get time relative to its begin
|
||||
time = aSource->StartTime + (state.SamplesPlayed % totalSamples) / static_cast<float>(Math::Max(1U, clipInfo.SampleRate));
|
||||
time = aSource->LastBufferStartTime + (state.SamplesPlayed % totalSamples) / static_cast<float>(Math::Max(1U, sampleRate));
|
||||
}
|
||||
return time;
|
||||
}
|
||||
@@ -697,10 +725,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(AudioSource* source)
|
||||
if (aSource && aSource->Voice)
|
||||
{
|
||||
const HRESULT hr = aSource->Voice->FlushSourceBuffers();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG(Warning, "XAudio2: FlushSourceBuffers failed. Error: 0x{0:x}", hr);
|
||||
}
|
||||
XAUDIO2_CHECK_ERROR(FlushSourceBuffers);
|
||||
aSource->BuffersProcessed = 0;
|
||||
}
|
||||
}
|
||||
@@ -749,8 +774,7 @@ void AudioBackendXAudio2::Buffer_Write(uint32 bufferId, byte* samples, const Aud
|
||||
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1];
|
||||
XAudio2::Locker.Unlock();
|
||||
|
||||
const uint32 bytesPerSample = info.BitDepth / 8;
|
||||
const int32 samplesLength = info.NumSamples * bytesPerSample;
|
||||
const uint32 samplesLength = info.NumSamples * info.BitDepth / 8;
|
||||
|
||||
aBuffer->Info = info;
|
||||
aBuffer->Data.Set(samples, samplesLength);
|
||||
@@ -779,7 +803,8 @@ void AudioBackendXAudio2::Base_SetVolume(float value)
|
||||
{
|
||||
if (XAudio2::MasteringVoice)
|
||||
{
|
||||
XAudio2::MasteringVoice->SetVolume(value);
|
||||
const HRESULT hr = XAudio2::MasteringVoice->SetVolume(value);
|
||||
XAUDIO2_CHECK_ERROR(SetVolume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -538,11 +538,7 @@ ContentLoadTask* Asset::createLoadingTask()
|
||||
|
||||
void Asset::startLoading()
|
||||
{
|
||||
// Check if is already loaded
|
||||
if (IsLoaded())
|
||||
return;
|
||||
|
||||
// Start loading (using async tasks)
|
||||
ASSERT(!IsLoaded());
|
||||
ASSERT(_loadingTask == nullptr);
|
||||
_loadingTask = createLoadingTask();
|
||||
ASSERT(_loadingTask != nullptr);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Engine/ShadersCompilation/Config.h"
|
||||
#if BUILD_DEBUG
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Scripting/BinaryModule.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -256,7 +257,9 @@ Asset::LoadResult Material::load()
|
||||
|
||||
#if BUILD_DEBUG && USE_EDITOR
|
||||
// Dump generated material source to the temporary file
|
||||
BinaryModule::Locker.Lock();
|
||||
source.SaveToFile(Globals::ProjectCacheFolder / TEXT("material.txt"));
|
||||
BinaryModule::Locker.Unlock();
|
||||
#endif
|
||||
|
||||
// Encrypt source code
|
||||
|
||||
@@ -154,7 +154,7 @@ void ContentService::LateUpdate()
|
||||
// Unload marked assets
|
||||
for (int32 i = 0; i < ToUnload.Count(); i++)
|
||||
{
|
||||
Asset* asset = ToUnload[i];
|
||||
Asset* asset = ToUnload[i];
|
||||
|
||||
// Check if has no references
|
||||
if (asset->GetReferencesCount() <= 0)
|
||||
@@ -521,37 +521,33 @@ Asset* Content::GetAsset(const Guid& id)
|
||||
|
||||
void Content::DeleteAsset(Asset* asset)
|
||||
{
|
||||
ScopeLock locker(AssetsLocker);
|
||||
|
||||
// Validate
|
||||
if (asset == nullptr || asset->_deleteFileOnUnload)
|
||||
{
|
||||
// Back
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(Info, "Deleting asset {0}...", asset->ToString());
|
||||
|
||||
// Ensure that asset is loaded (easier than cancel in-flight loading)
|
||||
asset->WaitForLoaded();
|
||||
|
||||
// Mark asset for delete queue (delete it after auto unload)
|
||||
asset->_deleteFileOnUnload = true;
|
||||
|
||||
// Unload
|
||||
UnloadAsset(asset);
|
||||
asset->DeleteObject();
|
||||
}
|
||||
|
||||
void Content::DeleteAsset(const StringView& path)
|
||||
{
|
||||
ScopeLock locker(AssetsLocker);
|
||||
|
||||
// Check if is loaded
|
||||
// Try to delete already loaded asset
|
||||
Asset* asset = GetAsset(path);
|
||||
if (asset != nullptr)
|
||||
{
|
||||
// Delete asset
|
||||
DeleteAsset(asset);
|
||||
return;
|
||||
}
|
||||
|
||||
ScopeLock locker(AssetsLocker);
|
||||
|
||||
// Remove from registry
|
||||
AssetInfo info;
|
||||
if (Cache.DeleteAsset(path, &info))
|
||||
@@ -573,7 +569,6 @@ void Content::deleteFileSafety(const StringView& path, const Guid& id)
|
||||
// Check if given id is invalid
|
||||
if (!id.IsValid())
|
||||
{
|
||||
// Cancel operation
|
||||
LOG(Warning, "Cannot remove file \'{0}\'. Given ID is invalid.", path);
|
||||
return;
|
||||
}
|
||||
@@ -585,7 +580,6 @@ void Content::deleteFileSafety(const StringView& path, const Guid& id)
|
||||
storage->CloseFileHandles(); // Close file handle to allow removing it
|
||||
if (!storage->HasAsset(id))
|
||||
{
|
||||
// Skip removing
|
||||
LOG(Warning, "Cannot remove file \'{0}\'. It doesn\'t contain asset {1}.", path, id);
|
||||
return;
|
||||
}
|
||||
@@ -703,13 +697,11 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat
|
||||
LOG(Warning, "Cannot copy file to destination.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (JsonStorageProxy::ChangeId(dstPath, dstId))
|
||||
{
|
||||
LOG(Warning, "Cannot change asset ID.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -774,12 +766,9 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat
|
||||
FileSystem::DeleteFile(tmpPath);
|
||||
|
||||
// Reload storage
|
||||
if (auto storage = ContentStorageManager::GetStorage(dstPath))
|
||||
{
|
||||
auto storage = ContentStorageManager::GetStorage(dstPath);
|
||||
if (storage)
|
||||
{
|
||||
storage->Reload();
|
||||
}
|
||||
storage->Reload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,10 +779,8 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat
|
||||
|
||||
void Content::UnloadAsset(Asset* asset)
|
||||
{
|
||||
// Check input
|
||||
if (asset == nullptr)
|
||||
return;
|
||||
|
||||
asset->DeleteObject();
|
||||
}
|
||||
|
||||
@@ -919,12 +906,8 @@ bool Content::IsAssetTypeIdInvalid(const ScriptingTypeHandle& type, const Script
|
||||
|
||||
Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
{
|
||||
// Early out
|
||||
if (!id.IsValid())
|
||||
{
|
||||
// Back
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if asset has been already loaded
|
||||
Asset* result = GetAsset(id);
|
||||
@@ -936,7 +919,6 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
LOG(Warning, "Different loaded asset type! Asset: \'{0}\'. Expected type: {1}", result->ToString(), type.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -954,12 +936,8 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
LoadCallAssetsLocker.Lock();
|
||||
const bool contains = LoadCallAssets.Contains(id);
|
||||
LoadCallAssetsLocker.Unlock();
|
||||
|
||||
if (!contains)
|
||||
{
|
||||
return GetAsset(id);
|
||||
}
|
||||
|
||||
Platform::Sleep(1);
|
||||
}
|
||||
}
|
||||
@@ -967,7 +945,6 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
{
|
||||
// Mark asset as loading
|
||||
LoadCallAssets.Add(id);
|
||||
|
||||
LoadCallAssetsLocker.Unlock();
|
||||
}
|
||||
|
||||
@@ -988,7 +965,7 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo&
|
||||
// Get cached asset info (from registry)
|
||||
if (!GetAssetInfo(id, assetInfo))
|
||||
{
|
||||
LOG(Warning, "Invalid or missing asset ({0}, {1}).", id.ToString(Guid::FormatType::N), type.ToString());
|
||||
LOG(Warning, "Invalid or missing asset ({0}, {1}).", id, type.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1032,11 +1009,13 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo&
|
||||
ASSERT(!Assets.ContainsKey(id));
|
||||
#endif
|
||||
Assets.Add(id, result);
|
||||
AssetsLocker.Unlock();
|
||||
|
||||
// Start asset loading
|
||||
// TODO: refactor this to create asset loading task-chain before AssetsLocker.Lock() to allow better parallelization
|
||||
result->startLoading();
|
||||
|
||||
AssetsLocker.Unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,13 @@ public:
|
||||
if (Asset)
|
||||
{
|
||||
Asset->Locker.Lock();
|
||||
Asset->_loadFailed = true;
|
||||
Asset->_isLoaded = false;
|
||||
LOG(Error, "Loading asset \'{0}\' result: {1}.", ToString(), ToString(Result::TaskFailed));
|
||||
Asset->_loadingTask = nullptr;
|
||||
if (Asset->_loadingTask == this)
|
||||
{
|
||||
Asset->_loadFailed = true;
|
||||
Asset->_isLoaded = false;
|
||||
LOG(Error, "Loading asset \'{0}\' result: {1}.", ToString(), ToString(Result::TaskFailed));
|
||||
Asset->_loadingTask = nullptr;
|
||||
}
|
||||
Asset->Locker.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -73,7 +76,10 @@ protected:
|
||||
{
|
||||
if (Asset)
|
||||
{
|
||||
Asset->_loadingTask = nullptr;
|
||||
Asset->Locker.Lock();
|
||||
if (Asset->_loadingTask == this)
|
||||
Asset->_loadingTask = nullptr;
|
||||
Asset->Locker.Unlock();
|
||||
Asset = nullptr;
|
||||
}
|
||||
|
||||
@@ -84,7 +90,10 @@ protected:
|
||||
{
|
||||
if (Asset)
|
||||
{
|
||||
Asset->_loadingTask = nullptr;
|
||||
Asset->Locker.Lock();
|
||||
if (Asset->_loadingTask == this)
|
||||
Asset->_loadingTask = nullptr;
|
||||
Asset->Locker.Unlock();
|
||||
Asset = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,8 @@ CreateAssetResult ImportModelFile::Import(CreateAssetContext& context)
|
||||
// Import model file
|
||||
ModelData modelData;
|
||||
String errorMsg;
|
||||
String autoImportOutput = String(StringUtils::GetDirectoryName(context.TargetAssetPath)) / StringUtils::GetFileNameWithoutExtension(context.InputPath);
|
||||
String autoImportOutput(StringUtils::GetDirectoryName(context.TargetAssetPath));
|
||||
autoImportOutput /= options.SubAssetFolder.HasChars() ? options.SubAssetFolder.TrimTrailing() : String(StringUtils::GetFileNameWithoutExtension(context.InputPath));
|
||||
if (ModelTool::ImportModel(context.InputPath, modelData, options, errorMsg, autoImportOutput))
|
||||
{
|
||||
LOG(Error, "Cannot import model file. {0}", errorMsg);
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
int32 _chunkIndex;
|
||||
int32 _index;
|
||||
|
||||
Iterator(ChunkedArray const* collection, const int32 index)
|
||||
Iterator(const ChunkedArray* collection, const int32 index)
|
||||
: _collection(const_cast<ChunkedArray*>(collection))
|
||||
, _chunkIndex(index / ChunkSize)
|
||||
, _index(index % ChunkSize)
|
||||
@@ -122,29 +122,29 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE ChunkedArray* GetChunkedArray() const
|
||||
Iterator(Iterator&& i)
|
||||
: _collection(i._collection)
|
||||
, _chunkIndex(i._chunkIndex)
|
||||
, _index(i._index)
|
||||
{
|
||||
return _collection;
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE int32 Index() const
|
||||
{
|
||||
return _chunkIndex * ChunkSize + _index;
|
||||
}
|
||||
|
||||
public:
|
||||
bool IsEnd() const
|
||||
FORCE_INLINE bool IsEnd() const
|
||||
{
|
||||
return Index() == _collection->Count();
|
||||
return (_chunkIndex * ChunkSize + _index) == _collection->_count;
|
||||
}
|
||||
|
||||
bool IsNotEnd() const
|
||||
FORCE_INLINE bool IsNotEnd() const
|
||||
{
|
||||
return Index() != _collection->Count();
|
||||
return (_chunkIndex * ChunkSize + _index) != _collection->_count;
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE T& operator*() const
|
||||
{
|
||||
return _collection->_chunks[_chunkIndex]->At(_index);
|
||||
@@ -155,7 +155,6 @@ public:
|
||||
return &_collection->_chunks[_chunkIndex]->At(_index);
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE bool operator==(const Iterator& v) const
|
||||
{
|
||||
return _collection == v._collection && _chunkIndex == v._chunkIndex && _index == v._index;
|
||||
@@ -166,17 +165,22 @@ public:
|
||||
return _collection != v._collection || _chunkIndex != v._chunkIndex || _index != v._index;
|
||||
}
|
||||
|
||||
public:
|
||||
Iterator& operator=(const Iterator& v)
|
||||
{
|
||||
_collection = v._collection;
|
||||
_chunkIndex = v._chunkIndex;
|
||||
_index = v._index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
// Check if it is not at end
|
||||
const int32 end = _collection->Count();
|
||||
if (Index() != end)
|
||||
if ((_chunkIndex * ChunkSize + _index) != _collection->_count)
|
||||
{
|
||||
// Move forward within chunk
|
||||
_index++;
|
||||
|
||||
// Check if need to change chunk
|
||||
if (_index == ChunkSize && _chunkIndex < _collection->_chunks.Count() - 1)
|
||||
{
|
||||
// Move to next chunk
|
||||
@@ -189,9 +193,9 @@ public:
|
||||
|
||||
Iterator operator++(int)
|
||||
{
|
||||
Iterator temp = *this;
|
||||
++temp;
|
||||
return temp;
|
||||
Iterator i = *this;
|
||||
++i;
|
||||
return i;
|
||||
}
|
||||
|
||||
Iterator& operator--()
|
||||
@@ -199,7 +203,6 @@ public:
|
||||
// Check if it's not at beginning
|
||||
if (_index != 0 || _chunkIndex != 0)
|
||||
{
|
||||
// Check if need to change chunk
|
||||
if (_index == 0)
|
||||
{
|
||||
// Move to previous chunk
|
||||
@@ -217,9 +220,9 @@ public:
|
||||
|
||||
Iterator operator--(int)
|
||||
{
|
||||
Iterator temp = *this;
|
||||
--temp;
|
||||
return temp;
|
||||
Iterator i = *this;
|
||||
--i;
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -294,7 +297,7 @@ public:
|
||||
{
|
||||
if (IsEmpty())
|
||||
return;
|
||||
ASSERT(i.GetChunkedArray() == this);
|
||||
ASSERT(i._collection == this);
|
||||
ASSERT(i._chunkIndex < _chunks.Count() && i._index < ChunkSize);
|
||||
ASSERT(i.Index() < Count());
|
||||
|
||||
@@ -432,11 +435,31 @@ public:
|
||||
|
||||
Iterator End() const
|
||||
{
|
||||
return Iterator(this, Count());
|
||||
return Iterator(this, _count);
|
||||
}
|
||||
|
||||
Iterator IteratorAt(int32 index) const
|
||||
{
|
||||
return Iterator(this, index);
|
||||
}
|
||||
|
||||
FORCE_INLINE Iterator begin()
|
||||
{
|
||||
return Iterator(this, 0);
|
||||
}
|
||||
|
||||
FORCE_INLINE Iterator end()
|
||||
{
|
||||
return Iterator(this, _count);
|
||||
}
|
||||
|
||||
FORCE_INLINE const Iterator begin() const
|
||||
{
|
||||
return Iterator(this, 0);
|
||||
}
|
||||
|
||||
FORCE_INLINE const Iterator end() const
|
||||
{
|
||||
return Iterator(this, _count);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Engine/Core/Config/Settings.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Content/SceneReference.h"
|
||||
@@ -76,6 +77,12 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(2010), EditorDisplay(\"Content\")")
|
||||
bool ShadersGenerateDebugData = false;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, skips bundling default engine fonts for UI. Use if to reduce build size if you don't use default engine fonts but custom ones only.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(2100), EditorDisplay(\"Content\")")
|
||||
bool SkipDefaultFonts = false;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, .NET Runtime won't be packaged with a game and will be required by user to be installed on system upon running game build. Available only on supported platforms such as Windows, Linux and macOS.
|
||||
/// </summary>
|
||||
@@ -106,6 +113,7 @@ public:
|
||||
DESERIALIZE(AdditionalAssetFolders);
|
||||
DESERIALIZE(ShadersNoOptimize);
|
||||
DESERIALIZE(ShadersGenerateDebugData);
|
||||
DESERIALIZE(SkipDefaultFonts);
|
||||
DESERIALIZE(SkipDotnetPackaging);
|
||||
DESERIALIZE(SkipUnusedDotnetLibsPackaging);
|
||||
}
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
#include "Log.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Core/Types/DateTime.h"
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "Engine/Serialization/FileWriteStream.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Debug/Exceptions/Exceptions.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
@@ -199,35 +198,36 @@ void Log::Logger::WriteFloor()
|
||||
void Log::Logger::ProcessLogMessage(LogType type, const StringView& msg, fmt_flax::memory_buffer& w)
|
||||
{
|
||||
const TimeSpan time = DateTime::Now() - LogStartTime;
|
||||
const int32 msgLength = msg.Length();
|
||||
|
||||
fmt_flax::format(w, TEXT("[ {0} ]: [{1}] "), *time.ToString('a'), ToString(type));
|
||||
|
||||
// On Windows convert all '\n' into '\r\n'
|
||||
#if PLATFORM_WINDOWS
|
||||
const int32 msgLength = msg.Length();
|
||||
if (msgLength > 1)
|
||||
bool hasWindowsNewLine = false;
|
||||
for (int32 i = 1; i < msgLength && !hasWindowsNewLine; i++)
|
||||
hasWindowsNewLine |= msg.Get()[i - 1] != '\r' && msg.Get()[i] == '\n';
|
||||
if (hasWindowsNewLine)
|
||||
{
|
||||
MemoryWriteStream msgStream(msgLength * sizeof(Char));
|
||||
msgStream.WriteChar(msg[0]);
|
||||
Array<Char> msgStream;
|
||||
msgStream.EnsureCapacity(msgLength);
|
||||
msgStream.Add(msg.Get()[0]);
|
||||
for (int32 i = 1; i < msgLength; i++)
|
||||
{
|
||||
if (msg[i - 1] != '\r' && msg[i] == '\n')
|
||||
msgStream.WriteChar(TEXT('\r'));
|
||||
msgStream.WriteChar(msg[i]);
|
||||
if (msg.Get()[i - 1] != '\r' && msg.Get()[i] == '\n')
|
||||
msgStream.Add(TEXT('\r'));
|
||||
msgStream.Add(msg.Get()[i]);
|
||||
}
|
||||
msgStream.WriteChar(msg[msgLength]);
|
||||
msgStream.WriteChar(TEXT('\0'));
|
||||
fmt_flax::format(w, TEXT("{}"), (const Char*)msgStream.GetHandle());
|
||||
//w.append(msgStream.GetHandle(), msgStream.GetHandle() + msgStream.GetPosition());
|
||||
msgStream.Add(TEXT('\0'));
|
||||
w.append(msgStream.Get(), (const Char*)(msgStream.Get() + msgStream.Count()));
|
||||
//fmt_flax::format(w, TEXT("{}"), (const Char*)msgStream.Get());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//w.append(msg.Get(), msg.Get() + msg.Length());
|
||||
fmt_flax::format(w, TEXT("{}"), msg);
|
||||
}
|
||||
#else
|
||||
fmt_flax::format(w, TEXT("{}"), msg);
|
||||
#endif
|
||||
|
||||
// Output raw message to the log
|
||||
w.append(msg.Get(), msg.Get() + msg.Length());
|
||||
//fmt_flax::format(w, TEXT("{}"), msg);
|
||||
}
|
||||
|
||||
void Log::Logger::Write(LogType type, const StringView& msg)
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
/// <param name="g">The green channel value.</param>
|
||||
/// <param name="b">The blue channel value.</param>
|
||||
/// <param name="a">The alpha channel value.</param>
|
||||
Color(float r, float g, float b, float a = 1)
|
||||
FORCE_INLINE Color(float r, float g, float b, float a = 1)
|
||||
: R(r)
|
||||
, G(g)
|
||||
, B(b)
|
||||
@@ -203,7 +203,7 @@ public:
|
||||
return Color(R - b.R, G - b.G, B - b.B, A - b.A);
|
||||
}
|
||||
|
||||
Color operator*(const Color& b) const
|
||||
FORCE_INLINE Color operator*(const Color& b) const
|
||||
{
|
||||
return Color(R * b.R, G * b.G, B * b.B, A * b.A);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Half.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Vector2.h"
|
||||
#include "Vector3.h"
|
||||
#include "Vector4.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Color.h"
|
||||
|
||||
static_assert(sizeof(Half) == 2, "Invalid Half type size.");
|
||||
@@ -16,12 +14,47 @@ Half2 Half2::Zero(0.0f, 0.0f);
|
||||
Half3 Half3::Zero(0.0f, 0.0f, 0.0f);
|
||||
Half4 Half4::Zero(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
Half2::Half2(const Float2& v)
|
||||
#if !USE_SSE_HALF_CONVERSION
|
||||
|
||||
Half Float16Compressor::Compress(float value)
|
||||
{
|
||||
X = Float16Compressor::Compress(v.X);
|
||||
Y = Float16Compressor::Compress(v.Y);
|
||||
Bits v, s;
|
||||
v.f = value;
|
||||
uint32 sign = v.si & signN;
|
||||
v.si ^= sign;
|
||||
sign >>= shiftSign; // logical shift
|
||||
s.si = mulN;
|
||||
s.si = static_cast<int32>(s.f * v.f); // correct subnormals
|
||||
v.si ^= (s.si ^ v.si) & -(minN > v.si);
|
||||
v.si ^= (infN ^ v.si) & -((infN > v.si) & (v.si > maxN));
|
||||
v.si ^= (nanN ^ v.si) & -((nanN > v.si) & (v.si > infN));
|
||||
v.ui >>= shift; // logical shift
|
||||
v.si ^= ((v.si - maxD) ^ v.si) & -(v.si > maxC);
|
||||
v.si ^= ((v.si - minD) ^ v.si) & -(v.si > subC);
|
||||
return v.ui | sign;
|
||||
}
|
||||
|
||||
float Float16Compressor::Decompress(Half value)
|
||||
{
|
||||
Bits v;
|
||||
v.ui = value;
|
||||
int32 sign = v.si & signC;
|
||||
v.si ^= sign;
|
||||
sign <<= shiftSign;
|
||||
v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC);
|
||||
v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC);
|
||||
Bits s;
|
||||
s.si = mulC;
|
||||
s.f *= v.si;
|
||||
const int32 mask = -(norC > v.si);
|
||||
v.si <<= shift;
|
||||
v.si ^= (s.si ^ v.si) & mask;
|
||||
v.si |= sign;
|
||||
return v.f;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Float2 Half2::ToFloat2() const
|
||||
{
|
||||
return Float2(
|
||||
@@ -30,13 +63,6 @@ Float2 Half2::ToFloat2() const
|
||||
);
|
||||
}
|
||||
|
||||
Half3::Half3(const Float3& v)
|
||||
{
|
||||
X = Float16Compressor::Compress(v.X);
|
||||
Y = Float16Compressor::Compress(v.Y);
|
||||
Z = Float16Compressor::Compress(v.Z);
|
||||
}
|
||||
|
||||
Float3 Half3::ToFloat3() const
|
||||
{
|
||||
return Float3(
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Math.h"
|
||||
#include "Vector2.h"
|
||||
#include "Vector3.h"
|
||||
|
||||
/// <summary>
|
||||
/// Half-precision 16 bit floating point number consisting of a sign bit, a 5 bit biased exponent, and a 10 bit mantissa
|
||||
@@ -45,54 +47,23 @@ class FLAXENGINE_API Float16Compressor
|
||||
static const int32 minD = minC - subC - 1;
|
||||
|
||||
public:
|
||||
static Half Compress(const float value)
|
||||
{
|
||||
#if USE_SSE_HALF_CONVERSION
|
||||
FORCE_INLINE static Half Compress(float value)
|
||||
{
|
||||
__m128 value1 = _mm_set_ss(value);
|
||||
__m128i value2 = _mm_cvtps_ph(value1, 0);
|
||||
return static_cast<Half>(_mm_cvtsi128_si32(value2));
|
||||
#else
|
||||
Bits v, s;
|
||||
v.f = value;
|
||||
uint32 sign = v.si & signN;
|
||||
v.si ^= sign;
|
||||
sign >>= shiftSign; // logical shift
|
||||
s.si = mulN;
|
||||
s.si = static_cast<int32>(s.f * v.f); // correct subnormals
|
||||
v.si ^= (s.si ^ v.si) & -(minN > v.si);
|
||||
v.si ^= (infN ^ v.si) & -((infN > v.si) & (v.si > maxN));
|
||||
v.si ^= (nanN ^ v.si) & -((nanN > v.si) & (v.si > infN));
|
||||
v.ui >>= shift; // logical shift
|
||||
v.si ^= ((v.si - maxD) ^ v.si) & -(v.si > maxC);
|
||||
v.si ^= ((v.si - minD) ^ v.si) & -(v.si > subC);
|
||||
return v.ui | sign;
|
||||
#endif
|
||||
}
|
||||
|
||||
static float Decompress(const Half value)
|
||||
FORCE_INLINE static float Decompress(Half value)
|
||||
{
|
||||
#if USE_SSE_HALF_CONVERSION
|
||||
__m128i value1 = _mm_cvtsi32_si128(static_cast<int>(value));
|
||||
__m128 value2 = _mm_cvtph_ps(value1);
|
||||
return _mm_cvtss_f32(value2);
|
||||
#else
|
||||
Bits v;
|
||||
v.ui = value;
|
||||
int32 sign = v.si & signC;
|
||||
v.si ^= sign;
|
||||
sign <<= shiftSign;
|
||||
v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC);
|
||||
v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC);
|
||||
Bits s;
|
||||
s.si = mulC;
|
||||
s.f *= v.si;
|
||||
const int32 mask = -(norC > v.si);
|
||||
v.si <<= shift;
|
||||
v.si ^= (s.si ^ v.si) & mask;
|
||||
v.si |= sign;
|
||||
return v.f;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static Half Compress(float value);
|
||||
static float Decompress(Half value);
|
||||
#endif
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -128,7 +99,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="x">X component</param>
|
||||
/// <param name="y">Y component</param>
|
||||
Half2(Half x, Half y)
|
||||
FORCE_INLINE Half2(Half x, Half y)
|
||||
: X(x)
|
||||
, Y(y)
|
||||
{
|
||||
@@ -139,7 +110,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="x">X component</param>
|
||||
/// <param name="y">Y component</param>
|
||||
Half2(float x, float y)
|
||||
FORCE_INLINE Half2(float x, float y)
|
||||
{
|
||||
X = Float16Compressor::Compress(x);
|
||||
Y = Float16Compressor::Compress(y);
|
||||
@@ -149,7 +120,11 @@ public:
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="v">X and Y components</param>
|
||||
Half2(const Float2& v);
|
||||
FORCE_INLINE Half2(const Float2& v)
|
||||
{
|
||||
X = Float16Compressor::Compress(v.X);
|
||||
Y = Float16Compressor::Compress(v.Y);
|
||||
}
|
||||
|
||||
public:
|
||||
Float2 ToFloat2() const;
|
||||
@@ -185,21 +160,26 @@ public:
|
||||
public:
|
||||
Half3() = default;
|
||||
|
||||
Half3(Half x, Half y, Half z)
|
||||
FORCE_INLINE Half3(Half x, Half y, Half z)
|
||||
: X(x)
|
||||
, Y(y)
|
||||
, Z(z)
|
||||
{
|
||||
}
|
||||
|
||||
Half3(const float x, const float y, const float z)
|
||||
FORCE_INLINE Half3(float x, float y, float z)
|
||||
{
|
||||
X = Float16Compressor::Compress(x);
|
||||
Y = Float16Compressor::Compress(y);
|
||||
Z = Float16Compressor::Compress(z);
|
||||
}
|
||||
|
||||
Half3(const Float3& v);
|
||||
FORCE_INLINE Half3(const Float3& v)
|
||||
{
|
||||
X = Float16Compressor::Compress(v.X);
|
||||
Y = Float16Compressor::Compress(v.Y);
|
||||
Z = Float16Compressor::Compress(v.Z);
|
||||
}
|
||||
|
||||
public:
|
||||
Float3 ToFloat3() const;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "Collections/Dictionary.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
|
||||
@@ -14,16 +13,15 @@ const Char* HertzSizesData[] = { TEXT("Hz"), TEXT("KHz"), TEXT("MHz"), TEXT("GHz
|
||||
Span<const Char*> Utilities::Private::BytesSizes(BytesSizesData, ARRAY_COUNT(BytesSizesData));
|
||||
Span<const Char*> Utilities::Private::HertzSizes(HertzSizesData, ARRAY_COUNT(HertzSizesData));
|
||||
|
||||
namespace ObjectsRemovalServiceImpl
|
||||
namespace
|
||||
{
|
||||
CriticalSection PoolLocker;
|
||||
DateTime LastUpdate;
|
||||
float LastUpdateGameTime;
|
||||
Dictionary<Object*, float> Pool(8192);
|
||||
uint64 PoolCounter = 0;
|
||||
}
|
||||
|
||||
using namespace ObjectsRemovalServiceImpl;
|
||||
|
||||
class ObjectsRemoval : public EngineService
|
||||
{
|
||||
public:
|
||||
@@ -64,6 +62,7 @@ void ObjectsRemovalService::Add(Object* obj, float timeToLive, bool useGameTime)
|
||||
|
||||
PoolLocker.Lock();
|
||||
Pool[obj] = timeToLive;
|
||||
PoolCounter++;
|
||||
PoolLocker.Unlock();
|
||||
}
|
||||
|
||||
@@ -72,6 +71,7 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta)
|
||||
PROFILE_CPU();
|
||||
|
||||
PoolLocker.Lock();
|
||||
PoolCounter = 0;
|
||||
|
||||
// Update timeouts and delete objects that timed out
|
||||
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
||||
@@ -90,6 +90,24 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta)
|
||||
}
|
||||
}
|
||||
|
||||
// If any object was added to the pool while removing objects (by this thread) then retry removing any nested objects (but without delta time)
|
||||
if (PoolCounter != 0)
|
||||
{
|
||||
RETRY:
|
||||
PoolCounter = 0;
|
||||
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value <= 0.0f)
|
||||
{
|
||||
Object* obj = i->Key;
|
||||
Pool.Remove(i);
|
||||
obj->OnDeleteObject();
|
||||
}
|
||||
}
|
||||
if (PoolCounter != 0)
|
||||
goto RETRY;
|
||||
}
|
||||
|
||||
PoolLocker.Unlock();
|
||||
}
|
||||
|
||||
@@ -121,7 +139,7 @@ void ObjectsRemoval::Dispose()
|
||||
|
||||
// Delete all remaining objects
|
||||
{
|
||||
ScopeLock lock(PoolLocker);
|
||||
PoolLocker.Lock();
|
||||
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
Object* obj = i->Key;
|
||||
@@ -129,6 +147,7 @@ void ObjectsRemoval::Dispose()
|
||||
obj->OnDeleteObject();
|
||||
}
|
||||
Pool.Clear();
|
||||
PoolLocker.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,26 @@ public:
|
||||
ASSERT(index >= 0 && index < _length);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
FORCE_INLINE T* begin()
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
FORCE_INLINE T* end()
|
||||
{
|
||||
return _data + _length;
|
||||
}
|
||||
|
||||
FORCE_INLINE const T* begin() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
FORCE_INLINE const T* end() const
|
||||
{
|
||||
return _data + _length;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -596,11 +596,13 @@ void EngineImpl::InitPaths()
|
||||
Globals::ProjectCacheFolder = Globals::ProjectFolder / TEXT("Cache");
|
||||
#endif
|
||||
|
||||
#if USE_MONO
|
||||
// We must ensure that engine is located in folder which path contains only ANSI characters
|
||||
// Why? Mono lib must have etc and lib folders at ANSI path
|
||||
// But project can be located on Unicode path
|
||||
if (!Globals::StartupFolder.IsANSI())
|
||||
Platform::Fatal(TEXT("Cannot start application in directory which name contains non-ANSI characters."));
|
||||
#endif
|
||||
|
||||
#if !PLATFORM_SWITCH && !FLAX_TESTS
|
||||
// Setup directories
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace FlaxEngine.Interop
|
||||
internal static void RegisterNativeLibrary(IntPtr moduleNamePtr, IntPtr modulePathPtr)
|
||||
{
|
||||
string moduleName = Marshal.PtrToStringAnsi(moduleNamePtr);
|
||||
string modulePath = Marshal.PtrToStringAnsi(modulePathPtr);
|
||||
string modulePath = Marshal.PtrToStringUni(modulePathPtr);
|
||||
libraryPaths[moduleName] = modulePath;
|
||||
}
|
||||
|
||||
@@ -857,36 +857,25 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr)
|
||||
internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, int fieldOffset, IntPtr valuePtr)
|
||||
{
|
||||
object fieldOwner = fieldOwnerHandle.Target;
|
||||
IntPtr fieldRef;
|
||||
#if USE_AOT
|
||||
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
||||
fieldRef = IntPtr.Zero;
|
||||
Debug.LogError("Not supported FieldGetValueReference");
|
||||
#else
|
||||
if (fieldOwner.GetType().IsValueType)
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference<object, IntPtr>(field.fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
fieldRef = FieldHelper.GetValueTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(field.fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void FieldGetValueReferenceWithOffset(ManagedHandle fieldOwnerHandle, int fieldOffset, IntPtr valuePtr)
|
||||
{
|
||||
object fieldOwner = fieldOwnerHandle.Target;
|
||||
if (fieldOwner.GetType().IsValueType)
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
fieldRef = FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
}
|
||||
#endif
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
|
||||
@@ -119,6 +119,7 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
}
|
||||
|
||||
#if !USE_AOT
|
||||
// Cache offsets to frequently accessed fields of FlaxEngine.Object
|
||||
private static int unmanagedPtrFieldOffset = IntPtr.Size + (Unsafe.Read<int>((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF);
|
||||
private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read<int>((typeof(FlaxEngine.Object).GetField("__internalId", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF);
|
||||
@@ -150,6 +151,7 @@ namespace FlaxEngine.Interop
|
||||
object obj = typeHolder.CreateScriptingObject(unmanagedPtr, idPtr);
|
||||
return ManagedHandle.Alloc(obj);
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static void* NativeAlloc(int byteCount)
|
||||
{
|
||||
@@ -429,6 +431,9 @@ namespace FlaxEngine.Interop
|
||||
/// </summary>
|
||||
internal static int GetFieldOffset(FieldInfo field, Type type)
|
||||
{
|
||||
if (field.IsLiteral)
|
||||
return 0;
|
||||
|
||||
// Get the address of the field, source: https://stackoverflow.com/a/56512720
|
||||
int fieldOffset = Unsafe.Read<int>((field.FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF;
|
||||
if (!type.IsValueType)
|
||||
@@ -436,6 +441,23 @@ namespace FlaxEngine.Interop
|
||||
return fieldOffset;
|
||||
}
|
||||
|
||||
#if USE_AOT
|
||||
/// <summary>
|
||||
/// Helper utility to set field of the referenced value via reflection.
|
||||
/// </summary>
|
||||
internal static void SetReferenceTypeField<T>(FieldInfo field, ref T fieldOwner, object fieldValue)
|
||||
{
|
||||
if (typeof(T).IsValueType)
|
||||
{
|
||||
// Value types need setting via boxed object to properly propagate value
|
||||
object fieldOwnerBoxed = fieldOwner;
|
||||
field.SetValue(fieldOwnerBoxed, fieldValue);
|
||||
fieldOwner = (T)fieldOwnerBoxed;
|
||||
}
|
||||
else
|
||||
field.SetValue(fieldOwner, fieldValue);
|
||||
}
|
||||
#else
|
||||
/// <summary>
|
||||
/// Returns a reference to the value of the field.
|
||||
/// </summary>
|
||||
@@ -462,6 +484,7 @@ namespace FlaxEngine.Interop
|
||||
byte* fieldPtr = (byte*)Unsafe.As<T, IntPtr>(ref fieldOwner) + fieldOffset;
|
||||
return ref Unsafe.AsRef<TField>(fieldPtr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -735,29 +758,49 @@ namespace FlaxEngine.Interop
|
||||
|
||||
private static void ToManagedFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
|
||||
{
|
||||
#if USE_AOT
|
||||
IntPtr fieldValue = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#else
|
||||
ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
fieldValueRef = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
|
||||
#endif
|
||||
fieldSize = IntPtr.Size;
|
||||
}
|
||||
|
||||
private static void ToManagedFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
|
||||
{
|
||||
#if USE_AOT
|
||||
IntPtr fieldValue = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#else
|
||||
ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
fieldValueRef = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
|
||||
#endif
|
||||
fieldSize = IntPtr.Size;
|
||||
}
|
||||
|
||||
private static void ToNativeFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
|
||||
{
|
||||
ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValueRef);
|
||||
#if USE_AOT
|
||||
object boxed = field.GetValue(fieldOwner);
|
||||
IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed));
|
||||
#else
|
||||
IntPtr fieldValue = FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValue);
|
||||
fieldSize = IntPtr.Size;
|
||||
}
|
||||
|
||||
private static void ToNativeFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
|
||||
{
|
||||
ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValueRef);
|
||||
#if USE_AOT
|
||||
object boxed = field.GetValue(fieldOwner);
|
||||
IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed));
|
||||
#else
|
||||
IntPtr fieldValue = FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValue);
|
||||
fieldSize = IntPtr.Size;
|
||||
}
|
||||
|
||||
@@ -799,8 +842,15 @@ namespace FlaxEngine.Interop
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
}
|
||||
|
||||
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField>.ToManaged(ref fieldValueRef, nativeFieldPtr, false);
|
||||
#if USE_AOT
|
||||
TField fieldValue = default;
|
||||
#else
|
||||
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField>.ToManaged(ref fieldValue, nativeFieldPtr, false);
|
||||
#if USE_AOT
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
|
||||
@@ -813,8 +863,15 @@ namespace FlaxEngine.Interop
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
}
|
||||
|
||||
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField>.ToManaged(ref fieldValueRef, nativeFieldPtr, false);
|
||||
#if USE_AOT
|
||||
TField fieldValue = default;
|
||||
#else
|
||||
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField>.ToManaged(ref fieldValue, nativeFieldPtr, false);
|
||||
#if USE_AOT
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
|
||||
@@ -825,8 +882,15 @@ namespace FlaxEngine.Interop
|
||||
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
|
||||
ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
TField[] fieldValue = (TField[])field.GetValue(fieldOwner);
|
||||
#else
|
||||
ref TField[] fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
|
||||
@@ -837,8 +901,15 @@ namespace FlaxEngine.Interop
|
||||
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
|
||||
ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
TField[] fieldValue = null;
|
||||
#else
|
||||
ref TField[] fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
|
||||
@@ -852,11 +923,11 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
#if USE_AOT
|
||||
TField fieldValueRef = (TField)field.GetValue(fieldOwner);
|
||||
TField fieldValue = (TField)field.GetValue(fieldOwner);
|
||||
#else
|
||||
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr);
|
||||
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
|
||||
}
|
||||
|
||||
internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
|
||||
@@ -870,11 +941,11 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
#if USE_AOT
|
||||
TField fieldValueRef = (TField)field.GetValue(fieldOwner);
|
||||
TField fieldValue = (TField)field.GetValue(fieldOwner);
|
||||
#else
|
||||
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr);
|
||||
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -904,8 +975,15 @@ namespace FlaxEngine.Interop
|
||||
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
|
||||
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
TField fieldValue = null;
|
||||
#else
|
||||
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
|
||||
@@ -915,8 +993,15 @@ namespace FlaxEngine.Interop
|
||||
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
|
||||
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
TField fieldValue = default;
|
||||
#else
|
||||
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
|
||||
@@ -926,8 +1011,15 @@ namespace FlaxEngine.Interop
|
||||
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
|
||||
ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
TField[] fieldValue = null;
|
||||
#else
|
||||
ref TField[] fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
|
||||
@@ -937,8 +1029,15 @@ namespace FlaxEngine.Interop
|
||||
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
|
||||
ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
TField[] fieldValue = null;
|
||||
#else
|
||||
ref TField[] fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
|
||||
#if USE_AOT
|
||||
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
|
||||
@@ -948,8 +1047,12 @@ namespace FlaxEngine.Interop
|
||||
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
|
||||
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr);
|
||||
#if USE_AOT
|
||||
TField fieldValue = (TField)field.GetValue(fieldOwner);
|
||||
#else
|
||||
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
|
||||
}
|
||||
|
||||
internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
|
||||
@@ -959,8 +1062,12 @@ namespace FlaxEngine.Interop
|
||||
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
|
||||
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
|
||||
|
||||
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr);
|
||||
#if USE_AOT
|
||||
TField fieldValue = (TField)field.GetValue(fieldOwner);
|
||||
#else
|
||||
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
|
||||
#endif
|
||||
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1044,7 +1151,7 @@ namespace FlaxEngine.Interop
|
||||
var fields = MarshalHelper<T>.marshallableFields;
|
||||
var offsets = MarshalHelper<T>.marshallableFieldOffsets;
|
||||
var marshallers = MarshalHelper<T>.toNativeFieldMarshallers;
|
||||
for (int i = 0; i < MarshalHelper<T>.marshallableFields.Length; i++)
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
{
|
||||
marshallers[i](fields[i], offsets[i], ref managedValue, nativePtr, out int fieldSize);
|
||||
nativePtr += fieldSize;
|
||||
@@ -1306,11 +1413,13 @@ namespace FlaxEngine.Interop
|
||||
return RuntimeHelpers.GetUninitializedObject(wrappedType);
|
||||
}
|
||||
|
||||
#if !USE_AOT
|
||||
internal object CreateScriptingObject(IntPtr unmanagedPtr, IntPtr idPtr)
|
||||
{
|
||||
object obj = CreateObject();
|
||||
object obj = RuntimeHelpers.GetUninitializedObject(wrappedType);
|
||||
if (obj is Object)
|
||||
{
|
||||
// TODO: use UnsafeAccessorAttribute on .NET 8 and use this path on all platforms (including non-Desktop, see MCore::ScriptingObject::CreateScriptingObject)
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<IntPtr>(unmanagedPtrFieldOffset, ref obj);
|
||||
fieldRef = unmanagedPtr;
|
||||
@@ -1331,8 +1440,9 @@ namespace FlaxEngine.Interop
|
||||
|
||||
return obj;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static implicit operator Type(TypeHolder holder) => holder?.type ?? null;
|
||||
public static implicit operator Type(TypeHolder holder) => holder?.type;
|
||||
public bool Equals(TypeHolder other) => type == other.type;
|
||||
public bool Equals(Type other) => type == other;
|
||||
public override int GetHashCode() => type.GetHashCode();
|
||||
|
||||
@@ -22,7 +22,7 @@ class ScreenService : public EngineService
|
||||
{
|
||||
public:
|
||||
ScreenService()
|
||||
: EngineService(TEXT("Screen"), 120)
|
||||
: EngineService(TEXT("Screen"), 500)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -634,15 +634,12 @@ void Foliage::RemoveFoliageType(int32 index)
|
||||
int32 Foliage::GetFoliageTypeInstancesCount(int32 index) const
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
int32 result = 0;
|
||||
|
||||
for (auto i = Instances.Begin(); i.IsNotEnd(); i++)
|
||||
for (auto i = Instances.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Type == index)
|
||||
result++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -503,6 +503,9 @@ void GPUDevice::DrawEnd()
|
||||
// Call present on all used tasks
|
||||
int32 presentCount = 0;
|
||||
bool anyVSync = false;
|
||||
#if COMPILE_WITH_PROFILER
|
||||
const double presentStart = Platform::GetTimeSeconds();
|
||||
#endif
|
||||
for (int32 i = 0; i < RenderTask::Tasks.Count(); i++)
|
||||
{
|
||||
const auto task = RenderTask::Tasks[i];
|
||||
@@ -537,6 +540,10 @@ void GPUDevice::DrawEnd()
|
||||
#endif
|
||||
GetMainContext()->Flush();
|
||||
}
|
||||
#if COMPILE_WITH_PROFILER
|
||||
const double presentEnd = Platform::GetTimeSeconds();
|
||||
ProfilerGPU::OnPresentTime((float)((presentEnd - presentStart) * 1000.0));
|
||||
#endif
|
||||
|
||||
_wasVSyncUsed = anyVSync;
|
||||
_isRendering = false;
|
||||
|
||||
@@ -259,6 +259,11 @@ API_STRUCT() struct GPULimits
|
||||
/// </summary>
|
||||
API_FIELD() bool HasDepthAsSRV;
|
||||
|
||||
/// <summary>
|
||||
/// True if device supports depth buffer clipping (see GPUPipelineState::Description::DepthClipEnable).
|
||||
/// </summary>
|
||||
API_FIELD() bool HasDepthClip;
|
||||
|
||||
/// <summary>
|
||||
/// True if device supports depth buffer texture as a readonly depth buffer (can be sampled in the shader while performing depth-test).
|
||||
/// </summary>
|
||||
|
||||
@@ -92,6 +92,8 @@ bool DecalMaterialShader::Load()
|
||||
{
|
||||
GPUPipelineState::Description psDesc0 = GPUPipelineState::Description::DefaultNoDepth;
|
||||
psDesc0.VS = _shader->GetVS("VS_Decal");
|
||||
if (psDesc0.VS == nullptr)
|
||||
return true;
|
||||
psDesc0.PS = _shader->GetPS("PS_Decal");
|
||||
psDesc0.CullMode = CullMode::Normal;
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ void DeferredMaterialShader::Unload()
|
||||
|
||||
bool DeferredMaterialShader::Load()
|
||||
{
|
||||
bool failed = false;
|
||||
auto psDesc = GPUPipelineState::Description::Default;
|
||||
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None;
|
||||
if (EnumHasAnyFlags(_info.FeaturesFlags, MaterialFeaturesFlags::DisableDepthTest))
|
||||
@@ -155,16 +156,20 @@ bool DeferredMaterialShader::Load()
|
||||
|
||||
// GBuffer Pass
|
||||
psDesc.VS = _shader->GetVS("VS");
|
||||
failed |= psDesc.VS == nullptr;
|
||||
psDesc.PS = _shader->GetPS("PS_GBuffer");
|
||||
_cache.Default.Init(psDesc);
|
||||
psDesc.VS = _shader->GetVS("VS", 1);
|
||||
failed |= psDesc.VS == nullptr;
|
||||
_cacheInstanced.Default.Init(psDesc);
|
||||
|
||||
// GBuffer Pass with lightmap (pixel shader permutation for USE_LIGHTMAP=1)
|
||||
psDesc.VS = _shader->GetVS("VS");
|
||||
failed |= psDesc.VS == nullptr;
|
||||
psDesc.PS = _shader->GetPS("PS_GBuffer", 1);
|
||||
_cache.DefaultLightmap.Init(psDesc);
|
||||
psDesc.VS = _shader->GetVS("VS", 1);
|
||||
failed |= psDesc.VS == nullptr;
|
||||
_cacheInstanced.DefaultLightmap.Init(psDesc);
|
||||
|
||||
// GBuffer Pass with skinning
|
||||
@@ -233,5 +238,5 @@ bool DeferredMaterialShader::Load()
|
||||
psDesc.VS = _shader->GetVS("VS_Skinned");
|
||||
_cache.DepthSkinned.Init(psDesc);
|
||||
|
||||
return false;
|
||||
return failed;
|
||||
}
|
||||
|
||||
@@ -174,6 +174,8 @@ bool ForwardMaterialShader::Load()
|
||||
|
||||
// Forward Pass
|
||||
psDesc.VS = _shader->GetVS("VS");
|
||||
if (psDesc.VS == nullptr)
|
||||
return true;
|
||||
psDesc.PS = _shader->GetPS("PS_Forward");
|
||||
psDesc.DepthWriteEnable = false;
|
||||
psDesc.BlendMode = BlendingMode::AlphaBlend;
|
||||
|
||||
@@ -339,7 +339,7 @@ namespace FlaxEngine
|
||||
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
/// <param name="colors">The vertex colors (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(Vector3[] vertices, int[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
|
||||
@@ -357,7 +357,7 @@ namespace FlaxEngine
|
||||
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
/// <param name="colors">The vertex colors (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(List<Vector3> vertices, List<int> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
|
||||
@@ -375,7 +375,7 @@ namespace FlaxEngine
|
||||
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
/// <param name="colors">The vertex colors (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(Vector3[] vertices, uint[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
|
||||
@@ -393,7 +393,7 @@ namespace FlaxEngine
|
||||
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
/// <param name="colors">The vertex colors (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(List<Vector3> vertices, List<uint> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
|
||||
@@ -411,7 +411,7 @@ namespace FlaxEngine
|
||||
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
/// <param name="colors">The vertex colors (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(Vector3[] vertices, ushort[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
|
||||
@@ -429,7 +429,7 @@ namespace FlaxEngine
|
||||
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
/// <param name="colors">The vertex colors (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(List<Vector3> vertices, List<ushort> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
|
||||
|
||||
@@ -609,7 +609,7 @@ bool Mesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& c
|
||||
ScopeLock lock(model->Locker);
|
||||
if (model->IsVirtual())
|
||||
{
|
||||
LOG(Error, "Cannot access CPU data of virtual models. Use GPU data download");
|
||||
LOG(Error, "Cannot access CPU data of virtual models. Use GPU data download.");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -318,7 +318,9 @@ void MeshData::BuildIndexBuffer()
|
||||
}
|
||||
|
||||
const auto endTime = Platform::GetTimeSeconds();
|
||||
LOG(Info, "Generated index buffer for mesh in {0}s ({1} vertices, {2} indices)", Utilities::RoundTo2DecimalPlaces(endTime - startTime), Positions.Count(), Indices.Count());
|
||||
const double time = Utilities::RoundTo2DecimalPlaces(endTime - startTime);
|
||||
if (time > 0.5f) // Don't log if generation was fast enough
|
||||
LOG(Info, "Generated {3} for mesh in {0}s ({1} vertices, {2} indices)", time, vertexCount, Indices.Count(), TEXT("indices"));
|
||||
}
|
||||
|
||||
void MeshData::FindPositions(const Float3& position, float epsilon, Array<int32>& result)
|
||||
@@ -449,7 +451,9 @@ bool MeshData::GenerateNormals(float smoothingAngle)
|
||||
}
|
||||
|
||||
const auto endTime = Platform::GetTimeSeconds();
|
||||
LOG(Info, "Generated tangents for mesh in {0}s ({1} vertices, {2} indices)", Utilities::RoundTo2DecimalPlaces(endTime - startTime), vertexCount, indexCount);
|
||||
const double time = Utilities::RoundTo2DecimalPlaces(endTime - startTime);
|
||||
if (time > 0.5f) // Don't log if generation was fast enough
|
||||
LOG(Info, "Generated {3} for mesh in {0}s ({1} vertices, {2} indices)", time, vertexCount, indexCount, TEXT("normals"));
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -685,7 +689,10 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
#endif
|
||||
|
||||
const auto endTime = Platform::GetTimeSeconds();
|
||||
LOG(Info, "Generated tangents for mesh in {0}s ({1} vertices, {2} indices)", Utilities::RoundTo2DecimalPlaces(endTime - startTime), vertexCount, indexCount);
|
||||
const double time = Utilities::RoundTo2DecimalPlaces(endTime - startTime);
|
||||
if (time > 0.5f) // Don't log if generation was fast enough
|
||||
LOG(Info, "Generated {3} for mesh in {0}s ({1} vertices, {2} indices)", time, vertexCount, indexCount, TEXT("tangents"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -872,7 +879,9 @@ void MeshData::ImproveCacheLocality()
|
||||
Allocator::Free(piCandidates);
|
||||
|
||||
const auto endTime = Platform::GetTimeSeconds();
|
||||
LOG(Info, "Cache relevant optimize for {0} vertices and {1} indices. Average output ACMR is {2}. Time: {3}s", vertexCount, indexCount, (float)iCacheMisses / indexCount / 3, Utilities::RoundTo2DecimalPlaces(endTime - startTime));
|
||||
const double time = Utilities::RoundTo2DecimalPlaces(endTime - startTime);
|
||||
if (time > 0.5f) // Don't log if generation was fast enough
|
||||
LOG(Info, "Generated {3} for mesh in {0}s ({1} vertices, {2} indices)", time, vertexCount, indexCount, TEXT("optimized indices"));
|
||||
}
|
||||
|
||||
float MeshData::CalculateTrianglesArea() const
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace FlaxEngine
|
||||
/// <param name="normals">The normal vectors (per vertex).</param>
|
||||
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(Vector3[] vertices, int[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv));
|
||||
@@ -235,7 +235,7 @@ namespace FlaxEngine
|
||||
/// <param name="normals">The normal vectors (per vertex).</param>
|
||||
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(Vector3[] vertices, uint[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv));
|
||||
@@ -254,7 +254,7 @@ namespace FlaxEngine
|
||||
/// <param name="normals">The normal vectors (per vertex).</param>
|
||||
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
|
||||
/// <param name="uv">The texture coordinates (per vertex).</param>
|
||||
[Obsolete("Deprecated in 1.4")]
|
||||
[Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
|
||||
public void UpdateMesh(Vector3[] vertices, ushort[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null)
|
||||
{
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv));
|
||||
|
||||
@@ -359,6 +359,7 @@ bool GPUDeviceDX11::Init()
|
||||
limits.HasAppendConsumeBuffers = true;
|
||||
limits.HasSeparateRenderTargetBlendState = true;
|
||||
limits.HasDepthAsSRV = true;
|
||||
limits.HasDepthClip = true;
|
||||
limits.HasReadOnlyDepth = true;
|
||||
limits.HasMultisampleDepthAsSRV = true;
|
||||
limits.HasTypedUAVLoad = featureDataD3D11Options2.TypedUAVLoadAdditionalFormats != 0;
|
||||
@@ -382,6 +383,7 @@ bool GPUDeviceDX11::Init()
|
||||
limits.HasAppendConsumeBuffers = false;
|
||||
limits.HasSeparateRenderTargetBlendState = false;
|
||||
limits.HasDepthAsSRV = false;
|
||||
limits.HasDepthClip = true;
|
||||
limits.HasReadOnlyDepth = createdFeatureLevel == D3D_FEATURE_LEVEL_10_1;
|
||||
limits.HasMultisampleDepthAsSRV = false;
|
||||
limits.HasTypedUAVLoad = false;
|
||||
|
||||
@@ -381,6 +381,7 @@ bool GPUDeviceDX12::Init()
|
||||
limits.HasAppendConsumeBuffers = true;
|
||||
limits.HasSeparateRenderTargetBlendState = true;
|
||||
limits.HasDepthAsSRV = true;
|
||||
limits.HasDepthClip = true;
|
||||
limits.HasReadOnlyDepth = true;
|
||||
limits.HasMultisampleDepthAsSRV = true;
|
||||
limits.HasTypedUAVLoad = options.TypedUAVLoadAdditionalFormats != 0;
|
||||
|
||||
@@ -50,18 +50,7 @@ bool GPUDeviceNull::Init()
|
||||
// Init device limits
|
||||
{
|
||||
auto& limits = Limits;
|
||||
limits.HasCompute = false;
|
||||
limits.HasTessellation = false;
|
||||
limits.HasGeometryShaders = false;
|
||||
limits.HasInstancing = false;
|
||||
limits.HasVolumeTextureRendering = false;
|
||||
limits.HasDrawIndirect = false;
|
||||
limits.HasAppendConsumeBuffers = false;
|
||||
limits.HasSeparateRenderTargetBlendState = false;
|
||||
limits.HasDepthAsSRV = false;
|
||||
limits.HasReadOnlyDepth = false;
|
||||
limits.HasMultisampleDepthAsSRV = false;
|
||||
limits.HasTypedUAVLoad = false;
|
||||
Platform::MemoryClear(&limits, sizeof(limits));
|
||||
limits.MaximumMipLevelsCount = 14;
|
||||
limits.MaximumTexture1DSize = 8192;
|
||||
limits.MaximumTexture1DArraySize = 512;
|
||||
@@ -70,11 +59,8 @@ bool GPUDeviceNull::Init()
|
||||
limits.MaximumTexture3DSize = 2048;
|
||||
limits.MaximumTextureCubeSize = 16384;
|
||||
limits.MaximumSamplerAnisotropy = 1;
|
||||
|
||||
for (int32 i = 0; i < static_cast<int32>(PixelFormat::MAX); i++)
|
||||
{
|
||||
FeaturesPerFormat[i] = FormatFeatures(static_cast<PixelFormat>(i), MSAALevel::None, FormatSupport::None);
|
||||
}
|
||||
}
|
||||
|
||||
// Create main context
|
||||
|
||||
@@ -1210,16 +1210,16 @@ void GPUContextVulkan::ResolveMultisample(GPUTexture* sourceMultisampleTexture,
|
||||
|
||||
void GPUContextVulkan::DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex)
|
||||
{
|
||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||
OnDrawCall();
|
||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||
vkCmdDraw(cmdBuffer->GetHandle(), verticesCount, instanceCount, startVertex, startInstance);
|
||||
RENDER_STAT_DRAW_CALL(verticesCount * instanceCount, verticesCount * instanceCount / 3);
|
||||
}
|
||||
|
||||
void GPUContextVulkan::DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex)
|
||||
{
|
||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||
OnDrawCall();
|
||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||
vkCmdDrawIndexed(cmdBuffer->GetHandle(), indicesCount, instanceCount, startIndex, startVertex, startInstance);
|
||||
RENDER_STAT_DRAW_CALL(0, indicesCount / 3 * instanceCount);
|
||||
}
|
||||
@@ -1227,10 +1227,9 @@ void GPUContextVulkan::DrawIndexedInstanced(uint32 indicesCount, uint32 instance
|
||||
void GPUContextVulkan::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs)
|
||||
{
|
||||
ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument));
|
||||
|
||||
OnDrawCall();
|
||||
auto bufferForArgsVK = (GPUBufferVulkan*)bufferForArgs;
|
||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||
OnDrawCall();
|
||||
vkCmdDrawIndirect(cmdBuffer->GetHandle(), bufferForArgsVK->GetHandle(), (VkDeviceSize)offsetForArgs, 1, sizeof(VkDrawIndirectCommand));
|
||||
RENDER_STAT_DRAW_CALL(0, 0);
|
||||
}
|
||||
@@ -1238,10 +1237,9 @@ void GPUContextVulkan::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 of
|
||||
void GPUContextVulkan::DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs)
|
||||
{
|
||||
ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument));
|
||||
|
||||
OnDrawCall();
|
||||
auto bufferForArgsVK = (GPUBufferVulkan*)bufferForArgs;
|
||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||
OnDrawCall();
|
||||
vkCmdDrawIndexedIndirect(cmdBuffer->GetHandle(), bufferForArgsVK->GetHandle(), (VkDeviceSize)offsetForArgs, 1, sizeof(VkDrawIndexedIndirectCommand));
|
||||
RENDER_STAT_DRAW_CALL(0, 0);
|
||||
}
|
||||
|
||||
@@ -1704,6 +1704,7 @@ bool GPUDeviceVulkan::Init()
|
||||
limits.HasDrawIndirect = PhysicalDeviceLimits.maxDrawIndirectCount >= 1;
|
||||
limits.HasAppendConsumeBuffers = false; // TODO: add Append Consume buffers support for Vulkan
|
||||
limits.HasSeparateRenderTargetBlendState = true;
|
||||
limits.HasDepthClip = PhysicalDeviceFeatures.depthClamp;
|
||||
limits.HasDepthAsSRV = true;
|
||||
limits.HasReadOnlyDepth = true;
|
||||
limits.HasMultisampleDepthAsSRV = !!PhysicalDeviceFeatures.sampleRateShading;
|
||||
|
||||
@@ -340,7 +340,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
||||
break;
|
||||
}
|
||||
_descRasterization.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
_descRasterization.depthClampEnable = !desc.DepthClipEnable;
|
||||
_descRasterization.depthClampEnable = !desc.DepthClipEnable && _device->Limits.HasDepthClip;
|
||||
_descRasterization.lineWidth = 1.0f;
|
||||
_desc.pRasterizationState = &_descRasterization;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ void GPUTimerQueryVulkan::Interrupt(CmdBufferVulkan* cmdBuffer)
|
||||
if (!_interrupted)
|
||||
{
|
||||
_interrupted = true;
|
||||
WriteTimestamp(cmdBuffer, _queries[_queryIndex].End);
|
||||
WriteTimestamp(cmdBuffer, _queries[_queryIndex].End, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ void GPUTimerQueryVulkan::Resume(CmdBufferVulkan* cmdBuffer)
|
||||
e.End.Pool = nullptr;
|
||||
|
||||
_interrupted = false;
|
||||
WriteTimestamp(cmdBuffer, e.Begin);
|
||||
WriteTimestamp(cmdBuffer, e.Begin, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
|
||||
|
||||
_queries.Add(e);
|
||||
_queryIndex++;
|
||||
@@ -56,13 +56,13 @@ bool GPUTimerQueryVulkan::GetResult(Query& query)
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUTimerQueryVulkan::WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query) const
|
||||
void GPUTimerQueryVulkan::WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query, VkPipelineStageFlagBits stage) const
|
||||
{
|
||||
auto pool = _device->FindAvailableTimestampQueryPool();
|
||||
uint32 index;
|
||||
pool->AcquireQuery(index);
|
||||
|
||||
vkCmdWriteTimestamp(cmdBuffer->GetHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, pool->GetHandle(), index);
|
||||
vkCmdWriteTimestamp(cmdBuffer->GetHandle(), stage, pool->GetHandle(), index);
|
||||
pool->MarkQueryAsStarted(index);
|
||||
|
||||
query.Pool = pool;
|
||||
@@ -168,7 +168,7 @@ void GPUTimerQueryVulkan::Begin()
|
||||
|
||||
_queryIndex = 0;
|
||||
_interrupted = false;
|
||||
WriteTimestamp(cmdBuffer, e.Begin);
|
||||
WriteTimestamp(cmdBuffer, e.Begin, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
|
||||
context->GetCmdBufferManager()->OnQueryBegin(this);
|
||||
|
||||
ASSERT(_queries.IsEmpty());
|
||||
@@ -193,7 +193,7 @@ void GPUTimerQueryVulkan::End()
|
||||
|
||||
if (!_interrupted)
|
||||
{
|
||||
WriteTimestamp(cmdBuffer, _queries[_queryIndex].End);
|
||||
WriteTimestamp(cmdBuffer, _queries[_queryIndex].End, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
|
||||
}
|
||||
context->GetCmdBufferManager()->OnQueryEnd(this);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
private:
|
||||
|
||||
bool GetResult(Query& query);
|
||||
void WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query) const;
|
||||
void WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query, VkPipelineStageFlagBits stage) const;
|
||||
bool TryGetResult();
|
||||
bool UseQueries();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user