Merge remote-tracking branch 'origin/1.12' into 1.13
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -47,8 +47,6 @@ namespace FlaxEditor.Content
|
||||
_preview = new CubeTexturePreview(false);
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -50,16 +50,12 @@ namespace FlaxEditor.Content
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
// Check if asset is streamed enough
|
||||
var asset = (IESProfile)request.Asset;
|
||||
return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
||||
return ThumbnailsModule.HasMinimumQuality((IESProfile)request.Asset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -55,8 +55,6 @@ namespace FlaxEditor.Content
|
||||
_preview = new MaterialPreview(false);
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
|
||||
// TODO: disable streaming for dependant assets during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -99,8 +99,6 @@ namespace FlaxEditor.Content
|
||||
_preview = new MaterialPreview(false);
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
|
||||
// TODO: disable streaming for dependant assets during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -78,8 +78,6 @@ namespace FlaxEditor.Content
|
||||
};
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -97,6 +95,7 @@ namespace FlaxEditor.Content
|
||||
var bounds = _preview.Model.GetBox();
|
||||
var maxSize = Math.Max(0.001f, (float)bounds.Size.MaxValue);
|
||||
_preview.ViewportCamera.SetArcBallView(bounds);
|
||||
_preview.NearPlane = Mathf.Min(10.0f, maxSize * 0.5f);
|
||||
_preview.FarPlane = Mathf.Max(1000.0f, maxSize * 2 + 100.0f);
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
|
||||
@@ -113,8 +113,6 @@ namespace FlaxEditor.Content
|
||||
_preview = new PrefabPreview(false);
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -124,20 +122,37 @@ namespace FlaxEditor.Content
|
||||
return false;
|
||||
|
||||
// Check if asset is streamed enough
|
||||
var asset = (Prefab)request.Asset;
|
||||
return asset.IsLoaded;
|
||||
return ThumbnailsModule.HasMinimumQuality((Prefab)request.Asset);
|
||||
}
|
||||
|
||||
private void Prepare(Actor actor)
|
||||
private void Prepare(Actor actor, ref BoundingBox bounds)
|
||||
{
|
||||
if (!actor.IsActive)
|
||||
return;
|
||||
if (actor is TextRender textRender)
|
||||
{
|
||||
textRender.UpdateLayout();
|
||||
}
|
||||
|
||||
// Use bounds from visual-only elements
|
||||
var actorBounds = BoundingBox.Empty;
|
||||
if (actor is ModelInstanceActor ||
|
||||
actor is SpriteRender ||
|
||||
actor is TextRender)
|
||||
{
|
||||
actorBounds = actor.EditorBox;
|
||||
}
|
||||
if (actorBounds != BoundingBox.Empty)
|
||||
{
|
||||
if (bounds == BoundingBox.Empty)
|
||||
bounds = actorBounds;
|
||||
else
|
||||
BoundingBox.Merge(ref actorBounds, ref bounds, out bounds);
|
||||
}
|
||||
|
||||
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||
{
|
||||
Prepare(actor.GetChild(i));
|
||||
Prepare(actor.GetChild(i), ref bounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,14 +180,24 @@ namespace FlaxEditor.Content
|
||||
else
|
||||
{
|
||||
// Update some actors data (some actor types update bounds/data later but its required to be done before rendering)
|
||||
Prepare(_preview.Instance);
|
||||
var bounds = BoundingBox.Empty;
|
||||
Prepare(_preview.Instance, ref bounds);
|
||||
//bounds = _preview.Instance.EditorBoxChildren;
|
||||
|
||||
// Auto fit actor to camera
|
||||
float targetSize = 30.0f;
|
||||
var bounds = _preview.Instance.EditorBoxChildren;
|
||||
var maxSize = Math.Max(0.001f, (float)bounds.Size.MaxValue);
|
||||
_preview.Instance.Scale = new Float3(targetSize / maxSize);
|
||||
_preview.Instance.Position = Vector3.Zero;
|
||||
if (bounds != BoundingBox.Empty)
|
||||
{
|
||||
float targetSize = 38.0f;
|
||||
float maxSize = Math.Max(0.001f, (float)bounds.Size.MaxValue);
|
||||
float scale = targetSize / maxSize;
|
||||
_preview.Instance.Scale = new Float3(scale);
|
||||
_preview.Instance.Position = -bounds.Center * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
_preview.Instance.Scale = Float3.One;
|
||||
_preview.Instance.Position = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
|
||||
@@ -101,8 +101,6 @@ namespace FlaxEditor.Content
|
||||
_preview = new AnimatedModelPreview(false);
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -50,16 +50,12 @@ namespace FlaxEditor.Content
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||
{
|
||||
// Check if asset is streamed enough
|
||||
var asset = (SpriteAtlas)request.Asset;
|
||||
return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
||||
return ThumbnailsModule.HasMinimumQuality((SpriteAtlas)request.Asset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -50,8 +50,6 @@ namespace FlaxEditor.Content
|
||||
Offsets = Margin.Zero,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: disable streaming for asset during thumbnail rendering (and restore it after)
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -122,39 +122,43 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
|
||||
internal static bool HasMinimumQuality(TextureBase asset)
|
||||
{
|
||||
// Don't block thumbnails queue when texture fails to stream in (eg. unsupported format)
|
||||
if (asset.HasStreamingError)
|
||||
return true; // Don't block thumbnails queue when texture fails to stream in (eg. unsupported format)
|
||||
return true;
|
||||
|
||||
// Check if enough mip levels are loaded
|
||||
var mipLevels = asset.MipLevels;
|
||||
var minMipLevels = Mathf.Min(mipLevels, 7);
|
||||
return asset.IsLoaded && asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * MinimumRequiredResourcesQuality));
|
||||
if (asset.IsLoaded && asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * MinimumRequiredResourcesQuality)))
|
||||
return true;
|
||||
|
||||
// Inform streaming about resource usage to stream it in for the thumbnail
|
||||
asset.SetStreamingVisible();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool HasMinimumQuality(Model asset)
|
||||
internal static bool HasMinimumQuality(ModelBase asset)
|
||||
{
|
||||
if (!asset.IsLoaded)
|
||||
return false;
|
||||
var lods = asset.LODs.Length;
|
||||
var lods = asset.LODsCount;
|
||||
var slots = asset.MaterialSlots;
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
if (slot.Material && !HasMinimumQuality(slot.Material))
|
||||
return false;
|
||||
}
|
||||
return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality));
|
||||
}
|
||||
|
||||
internal static bool HasMinimumQuality(SkinnedModel asset)
|
||||
{
|
||||
var lods = asset.LODs.Length;
|
||||
if (asset.IsLoaded && lods == 0)
|
||||
return true; // Skeleton-only model
|
||||
var slots = asset.MaterialSlots;
|
||||
// Check if all materials are loaded (incl. dependent resources)
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
if (slot.Material && !HasMinimumQuality(slot.Material))
|
||||
return false;
|
||||
}
|
||||
return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality));
|
||||
|
||||
// Check if enough LODs are loaded
|
||||
if (asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality)))
|
||||
return true;
|
||||
|
||||
// TODO: impl SetStreamingVisible for models similar to textures (ModelsStreamingHandler needs to use it)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool HasMinimumQuality(MaterialBase asset)
|
||||
@@ -177,6 +181,14 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
return baseMaterial == null || HasMinimumQualityInternal(baseMaterial);
|
||||
}
|
||||
|
||||
internal static bool HasMinimumQuality(Prefab asset)
|
||||
{
|
||||
if (!asset.IsLoaded)
|
||||
return false;
|
||||
var defaultInstance = asset.GetDefaultInstance();
|
||||
return defaultInstance == null || HasMinimumQualityInternal(defaultInstance);
|
||||
}
|
||||
|
||||
private static bool HasMinimumQualityInternal(MaterialBase asset)
|
||||
{
|
||||
if (!asset.IsLoaded)
|
||||
@@ -190,6 +202,43 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HasMinimumQualityInternal(Actor actor)
|
||||
{
|
||||
if (!actor.IsActive)
|
||||
return true;
|
||||
|
||||
if (actor is ModelInstanceActor modelInstance)
|
||||
{
|
||||
var model = modelInstance.GetModel();
|
||||
if (model && !HasMinimumQuality(model))
|
||||
return false;
|
||||
var slots = modelInstance.MaterialSlots;
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
if (slot.Material && !HasMinimumQuality(slot.Material))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (actor is SpriteRender spriteRender)
|
||||
{
|
||||
if (spriteRender.Material && !HasMinimumQuality(spriteRender.Material))
|
||||
return false;
|
||||
}
|
||||
if (actor is TextRender textRender)
|
||||
{
|
||||
if (textRender.Material && !HasMinimumQuality(textRender.Material))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < actor.ChildrenCount; i++)
|
||||
{
|
||||
if (!HasMinimumQualityInternal(actor.GetChild(i)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IContentItemOwner
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -128,34 +128,15 @@ public sealed class ContentItemTreeNode : TreeNode, IContentItemOwner
|
||||
return base.OnMouseDoubleClickHeader(ref location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (IsFocused)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.Return:
|
||||
Editor.Instance.Windows.ContentWin.Open(Item);
|
||||
return true;
|
||||
case KeyboardKeys.F2:
|
||||
Editor.Instance.Windows.ContentWin.Rename(Item);
|
||||
return true;
|
||||
case KeyboardKeys.Delete:
|
||||
Editor.Instance.Windows.ContentWin.Delete(Item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DoDragDrop()
|
||||
{
|
||||
DoDragDrop(DragItems.GetDragData(Item));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool ShowTooltip => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnShowTooltip(out string text, out Float2 location, out Rectangle area)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Content;
|
||||
|
||||
/// <summary>
|
||||
/// The content tree view panel.
|
||||
/// </summary>
|
||||
public class TreeViewPanel : Panel
|
||||
{
|
||||
/// <summary>
|
||||
/// The content tree assigned to this panel.
|
||||
/// </summary>
|
||||
public Tree ContentTree;
|
||||
|
||||
private InputActionsContainer _inputActions;
|
||||
private bool _isCutting;
|
||||
private List<ContentItem> _cutItems = new List<ContentItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeViewPanel"/> class.
|
||||
/// </summary>
|
||||
public TreeViewPanel()
|
||||
: base(ScrollBars.None)
|
||||
{
|
||||
// Setup input actions
|
||||
_inputActions = new InputActionsContainer(new[]
|
||||
{
|
||||
new InputActionsContainer.Binding(options => options.Rename, Rename),
|
||||
new InputActionsContainer.Binding(options => options.Delete, Delete),
|
||||
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
|
||||
new InputActionsContainer.Binding(options => options.Copy, Copy),
|
||||
new InputActionsContainer.Binding(options => options.Paste, Paste),
|
||||
new InputActionsContainer.Binding(options => options.Cut, Cut),
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames the selected item.
|
||||
/// </summary>
|
||||
public void Rename()
|
||||
{
|
||||
if (ContentTree == null || !Visible)
|
||||
return;
|
||||
|
||||
var selection = ContentTree.Selection;
|
||||
if (selection.Count > 0)
|
||||
{
|
||||
var node = selection[0];
|
||||
if (node is ContentItemTreeNode contentNode)
|
||||
{
|
||||
Editor.Instance.Windows.ContentWin.Rename(contentNode.Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the selected items.
|
||||
/// </summary>
|
||||
public void Delete()
|
||||
{
|
||||
if (ContentTree == null || !Visible)
|
||||
return;
|
||||
|
||||
var selection = ContentTree.Selection;
|
||||
if (selection.Count > 0)
|
||||
{
|
||||
var items = new List<ContentItem>();
|
||||
foreach (var node in selection)
|
||||
{
|
||||
if (node is ContentItemTreeNode fileNode)
|
||||
{
|
||||
items.Add(fileNode.Item);
|
||||
} else if (node is ContentFolderTreeNode folderNode)
|
||||
{
|
||||
items.Add(folderNode.Folder);
|
||||
}
|
||||
}
|
||||
|
||||
Editor.Instance.Windows.ContentWin.Delete(items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates the selected items.
|
||||
/// </summary>
|
||||
public void Duplicate()
|
||||
{
|
||||
if (ContentTree == null || !Visible)
|
||||
return;
|
||||
|
||||
var selection = ContentTree.Selection;
|
||||
if (selection.Count > 0)
|
||||
{
|
||||
var items = new List<ContentItem>();
|
||||
foreach (var node in selection)
|
||||
{
|
||||
if (node is ContentItemTreeNode contentNode)
|
||||
{
|
||||
items.Add(contentNode.Item);
|
||||
}
|
||||
}
|
||||
|
||||
Editor.Instance.Windows.ContentWin.Duplicate(items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the items.
|
||||
/// </summary>
|
||||
public void Copy()
|
||||
{
|
||||
if (ContentTree == null || !Visible)
|
||||
return;
|
||||
|
||||
var selection = ContentTree.Selection;
|
||||
if (selection.Count == 0)
|
||||
return;
|
||||
var filePaths = new List<string>();
|
||||
foreach (var node in selection)
|
||||
if (node is ContentItemTreeNode contentNode)
|
||||
filePaths.Add(contentNode.Item.Path);
|
||||
|
||||
Clipboard.Files = filePaths.ToArray();
|
||||
UpdateContentItemCut(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pastes the items.
|
||||
/// </summary>
|
||||
public void Paste()
|
||||
{
|
||||
if (ContentTree == null || !Visible)
|
||||
return;
|
||||
|
||||
var files = Clipboard.Files;
|
||||
if (files == null || files.Length == 0)
|
||||
return;
|
||||
|
||||
Editor.Instance.Windows.ContentWin.Paste(files, _isCutting);
|
||||
UpdateContentItemCut(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cuts the items.
|
||||
/// </summary>
|
||||
public void Cut()
|
||||
{
|
||||
if (ContentTree == null || !Visible)
|
||||
return;
|
||||
|
||||
Copy();
|
||||
UpdateContentItemCut(true);
|
||||
}
|
||||
|
||||
private void UpdateContentItemCut(bool cut)
|
||||
{
|
||||
_isCutting = cut;
|
||||
|
||||
// Add selection to cut list
|
||||
if (cut)
|
||||
{
|
||||
var selection = ContentTree.Selection;
|
||||
foreach (var node in selection)
|
||||
{
|
||||
if (node is ContentItemTreeNode contentNode)
|
||||
_cutItems.Add(contentNode.Item);
|
||||
}
|
||||
}
|
||||
|
||||
// Update item with if it is being cut.
|
||||
foreach (var item in _cutItems)
|
||||
{
|
||||
item.IsBeingCut = cut;
|
||||
}
|
||||
|
||||
// Clean up cut items
|
||||
if (!cut)
|
||||
_cutItems.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (!Visible)
|
||||
return false;
|
||||
if (ContentTree == null)
|
||||
return base.OnKeyDown(key);
|
||||
|
||||
if (_inputActions.Process(Editor.Instance, this, key))
|
||||
return true;
|
||||
|
||||
var selection = ContentTree.Selection;
|
||||
if (selection.Count > 0)
|
||||
{
|
||||
if (key == KeyboardKeys.Return)
|
||||
{
|
||||
foreach (var node in selection)
|
||||
{
|
||||
if (node is ContentItemTreeNode contentNode)
|
||||
Editor.Instance.Windows.ContentWin.Open(contentNode.Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
}
|
||||
@@ -183,16 +183,33 @@ bool WebPlatformTools::OnPostProcess(CookingData& data)
|
||||
String emscriptenSdk = TEXT("EMSDK");
|
||||
Platform::GetEnvironmentVariable(emscriptenSdk, emscriptenSdk);
|
||||
procSettings.FileName = emscriptenSdk / TEXT("upstream/emscripten/tools/file_packager");
|
||||
#if PLATFORM_WIN32
|
||||
procSettings.FileName += TEXT(".bat");
|
||||
#endif
|
||||
procSettings.Arguments = String::Format(TEXT("files.data --preload \"{}@/\" --lz4 --js-output=files.js"), data.DataOutputPath);
|
||||
procSettings.WorkingDirectory = data.OriginalOutputPath;
|
||||
#if PLATFORM_MAC || PLATFORM_WINDOWS
|
||||
// Use python bundled with SDK (python from min-spec XCode 16.4 is 3.9.6 which is < 3.10 needed by SDK 5.0)
|
||||
Array<String> pythons;
|
||||
FileSystem::GetChildDirectories(pythons, emscriptenSdk / TEXT("/python"));
|
||||
if (pythons.HasItems())
|
||||
{
|
||||
procSettings.Arguments = procSettings.FileName + TEXT(".py ") + procSettings.Arguments;
|
||||
#if PLATFORM_WINDOWS
|
||||
procSettings.FileName = pythons[0] / TEXT("/python.exe");
|
||||
#else
|
||||
procSettings.FileName = pythons[0] / TEXT("/bin/python3");
|
||||
#endif
|
||||
}
|
||||
#if PLATFORM_WINDOWS
|
||||
else
|
||||
{
|
||||
procSettings.FileName += TEXT(".bat");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
const int32 result = Platform::CreateProcess(procSettings);
|
||||
if (result != 0)
|
||||
{
|
||||
if (!FileSystem::FileExists(procSettings.FileName))
|
||||
data.Error(TEXT("Missing file_packager.bat. Ensure Emscripten SDK installation is valid and 'EMSDK' environment variable points to it."));
|
||||
data.Error(TEXT("Missing file_packager tool. Ensure Emscripten SDK installation is valid and 'EMSDK' environment variable points to it."));
|
||||
data.Error(String::Format(TEXT("Failed to package project files (result code: {0}). See log for more info."), result));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -73,21 +73,19 @@ public class Editor : EditorModule
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "PS4", "PLATFORM_TOOLS_PS4");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "PS5", "PLATFORM_TOOLS_PS5");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "XboxScarlett", "PLATFORM_TOOLS_XBOX_SCARLETT", "PLATFORM_TOOLS_GDK");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Switch", "PLATFORM_TOOLS_SWITCH");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX");
|
||||
}
|
||||
else if (options.Platform.Target == TargetPlatform.Linux)
|
||||
{
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID");
|
||||
}
|
||||
else if (options.Platform.Target == TargetPlatform.Mac)
|
||||
{
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Mac", "PLATFORM_TOOLS_MAC");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "iOS", "PLATFORM_TOOLS_IOS");
|
||||
}
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Web", "PLATFORM_TOOLS_WEB");
|
||||
|
||||
// Visual Studio integration
|
||||
@@ -118,11 +116,11 @@ public class Editor : EditorModule
|
||||
{
|
||||
files.Add(Path.Combine(FolderPath, "Editor.h"));
|
||||
files.Add(Path.Combine(FolderPath, "ProjectInfo.h"));
|
||||
|
||||
files.Add(Path.Combine(FolderPath, "Cooker/CookingData.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Cooker/GameCooker.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Cooker/PlatformTools.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Cooker/Steps/CookAssetsStep.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Utilities/ViewportIconsRenderer.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Managed/ManagedEditor.h"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,11 +38,6 @@ namespace FlaxEditor.GUI
|
||||
/// </summary>
|
||||
public event Action SelectedItemChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for assets validation. Cane be used to implement a rule for assets to pick.
|
||||
/// </summary>
|
||||
public Func<ContentItem, bool> CheckValid;
|
||||
|
||||
/// <summary>
|
||||
/// False if changing selected item is disabled.
|
||||
/// </summary>
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
// Check if is docked to the floating window and is selected so update window title
|
||||
if (IsSelected && _dockedTo is FloatWindowDockPanel floatPanel)
|
||||
{
|
||||
floatPanel.Window.Title = Title;
|
||||
floatPanel.UpdateTitle(_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +200,14 @@ namespace FlaxEditor.GUI.Docking
|
||||
Dispose();
|
||||
}
|
||||
|
||||
internal void UpdateTitle(string title)
|
||||
{
|
||||
_window.Title = title;
|
||||
var decorations = Parent.GetChild<FloatWindowDecorations>();
|
||||
if (decorations != null)
|
||||
decorations.PerformLayout();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsFloating => true;
|
||||
|
||||
@@ -227,10 +235,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
|
||||
if (_window != null && SelectedTab != null)
|
||||
{
|
||||
_window.Title = SelectedTab.Title;
|
||||
var decorations = Parent.GetChild<FloatWindowDecorations>();
|
||||
if (decorations != null)
|
||||
decorations.PerformLayout();
|
||||
UpdateTitle(SelectedTab.Title);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,8 @@ namespace FlaxEditor.Gizmo
|
||||
_isDisabled = ShouldGizmoBeLocked();
|
||||
|
||||
float brightness = _isDisabled ? options.Visual.TransformGizmoBrightnessDisabled : options.Visual.TransformGizmoBrightness;
|
||||
if (Mathf.NearEqual(brightness, (float)_materialAxisX.GetParameterValue(_brightnessParamName)))
|
||||
var currentValue = _materialAxisX.GetParameterValue(_brightnessParamName);
|
||||
if (currentValue is not float currentValueFloat || Mathf.NearEqual(brightness, currentValueFloat))
|
||||
return;
|
||||
_materialAxisX.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisY.SetParameterValue(_brightnessParamName, brightness);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/ShadowsOfMordor/Types.h"
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
#include "Engine/Tools/ModelTool/ModelTool.h"
|
||||
#include "Engine/Tools/AudioTool/AudioTool.h"
|
||||
|
||||
@@ -499,6 +499,7 @@ namespace FlaxEditor.Modules
|
||||
InitWindowDecorations(mainWindow);
|
||||
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
Editor.CodeEditing.SelectedEditorChanged += OnSelectedCodeEditorChanged;
|
||||
|
||||
mainWindow.PerformLayout(true);
|
||||
}
|
||||
@@ -870,6 +871,11 @@ namespace FlaxEditor.Modules
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
private void OnSelectedCodeEditorChanged(SourceCodeEditing.ISourceCodeEditor codeEditor)
|
||||
{
|
||||
_menuFileOpenScriptsProject.Text = $"Open scripts project ({(codeEditor?.Name ?? "Default")})";
|
||||
}
|
||||
|
||||
private void InitToolstrip(RootControl mainWindow)
|
||||
{
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace FlaxEditor.States
|
||||
IsPlayModeStarting = false;
|
||||
Profiler.EndEvent();
|
||||
|
||||
Time.Synchronize();
|
||||
Time.Synchronize(true);
|
||||
}
|
||||
|
||||
private void SetupEditorEnvOptions()
|
||||
@@ -213,7 +213,7 @@ namespace FlaxEditor.States
|
||||
IsPlayModeEnding = false;
|
||||
Profiler.EndEvent();
|
||||
|
||||
Time.Synchronize();
|
||||
Time.Synchronize(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,6 +1138,21 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Output(0, "", typeof(float), 0),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 37,
|
||||
Title = "Instance Transform",
|
||||
Description = "Animated model transformation (in world space).",
|
||||
Flags = NodeFlags.AnimGraph,
|
||||
Size = new Float2(200, 80),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Output(0, "", typeof(Transform), 0),
|
||||
NodeElementArchetype.Factory.Output(1, "Position", typeof(Vector3), 1),
|
||||
NodeElementArchetype.Factory.Output(2, "Rotation", typeof(Quaternion), 2),
|
||||
NodeElementArchetype.Factory.Output(3, "Scale", typeof(Float3), 3),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,5 +48,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
[NoSerialize, HideInEditor]
|
||||
public readonly SurfaceMeta Meta = new SurfaceMeta();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} {Name} = {Value?.ToString() ?? "null"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
public GraphParameter Parameter;
|
||||
public bool IsPublic;
|
||||
public int Index;
|
||||
public Type Type;
|
||||
public object Tag;
|
||||
public Attribute[] Attributes;
|
||||
@@ -31,15 +32,16 @@ namespace FlaxEditor.Surface
|
||||
public HeaderAttribute Header;
|
||||
public string DisplayName;
|
||||
|
||||
public GraphParameterData(GraphParameter parameter, object tag = null)
|
||||
: this(parameter, null, parameter.IsPublic, parameter.Type, SurfaceMeta.GetAttributes(parameter), tag)
|
||||
public GraphParameterData(GraphParameter parameter, int index, object tag = null)
|
||||
: this(parameter, index, null, parameter.IsPublic, parameter.Type, SurfaceMeta.GetAttributes(parameter), tag)
|
||||
{
|
||||
}
|
||||
|
||||
public GraphParameterData(GraphParameter parameter, string name, bool isPublic, Type type, Attribute[] attributes, object tag)
|
||||
public GraphParameterData(GraphParameter parameter, int index, string name, bool isPublic, Type type, Attribute[] attributes, object tag)
|
||||
{
|
||||
Parameter = parameter;
|
||||
IsPublic = isPublic;
|
||||
Index = index;
|
||||
Type = type;
|
||||
Tag = tag;
|
||||
Attributes = attributes;
|
||||
@@ -74,8 +76,14 @@ namespace FlaxEditor.Surface
|
||||
if (Editor.Instance.Options.Options.General.ScriptMembersOrder == GeneralOptions.MembersOrder.Alphabetical)
|
||||
return string.Compare(x.DisplayName, y.DisplayName, StringComparison.InvariantCulture);
|
||||
|
||||
// Keep same order
|
||||
return 0;
|
||||
// Keep same order (from input indices)
|
||||
return x.Index - y.Index;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} {DisplayName}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +214,7 @@ namespace FlaxEditor.Surface
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
var parameterId = parameter.ParameterID;
|
||||
@@ -229,7 +238,7 @@ namespace FlaxEditor.Surface
|
||||
surfaceParameters.Add(surfaceParameter);
|
||||
}
|
||||
var attributes = surfaceParameter?.Meta.GetAttributes() ?? FlaxEngine.Utils.GetEmptyArray<Attribute>();
|
||||
data[i] = new GraphParameterData(null, parameter.Name, parameter.IsPublic, ToType(parameter.ParameterType), attributes, parameter);
|
||||
data[i] = new GraphParameterData(null, index++, parameter.Name, parameter.IsPublic, ToType(parameter.ParameterType), attributes, parameter);
|
||||
i++;
|
||||
}
|
||||
Array.Sort(data, GraphParameterData.Compare);
|
||||
@@ -243,7 +252,7 @@ namespace FlaxEditor.Surface
|
||||
int i = 0;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
data[i] = new GraphParameterData(parameter.EmitterParameter, parameter);
|
||||
data[i] = new GraphParameterData(parameter.EmitterParameter, i, parameter);
|
||||
i++;
|
||||
}
|
||||
Array.Sort(data, GraphParameterData.Compare);
|
||||
@@ -257,7 +266,7 @@ namespace FlaxEditor.Surface
|
||||
int i = 0;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
data[i] = new GraphParameterData(parameter);
|
||||
data[i] = new GraphParameterData(parameter, i);
|
||||
i++;
|
||||
}
|
||||
Array.Sort(data, GraphParameterData.Compare);
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
|
||||
private bool _showNodes, _showBounds, _showFloor, _showNodesNames;
|
||||
private StaticModel _floorModel;
|
||||
private bool _playAnimation, _playAnimationOnce;
|
||||
private bool _playAnimation, _playAnimationOnce, _autoAdjustCamera = true;
|
||||
private float _playSpeed = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
@@ -291,6 +291,13 @@ namespace FlaxEditor.Viewport.Previews
|
||||
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
{
|
||||
if (_autoAdjustCamera && SkinnedModel && SkinnedModel.IsLoaded)
|
||||
{
|
||||
// Control camera's near/far planes to properly cover object
|
||||
_autoAdjustCamera = false;
|
||||
ModelPreview.AdjustCamera(this, SkinnedModel.GetBox());
|
||||
}
|
||||
|
||||
if (!ScaleToFit)
|
||||
{
|
||||
if (_snapToOrigin)
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
private ContextMenuButton _showBoundsButton, _showCurrentLODButton, _showNormalsButton, _showTangentsButton, _showBitangentsButton, _showFloorButton;
|
||||
private ContextMenu _previewLODsWidgetButtonMenu;
|
||||
private StaticModel _previewModel, _floorModel;
|
||||
private bool _showBounds, _showCurrentLOD, _showNormals, _showTangents, _showBitangents, _showFloor;
|
||||
private bool _showBounds, _showCurrentLOD, _showNormals, _showTangents, _showBitangents, _showFloor, _autoAdjustCamera = true;
|
||||
private MeshDataCache _meshDatas;
|
||||
|
||||
/// <summary>
|
||||
@@ -221,8 +221,8 @@ namespace FlaxEditor.Viewport.Previews
|
||||
_showCurrentLODButton.IndexInParent = 2;
|
||||
_showCurrentLODButton.CloseMenuOnClick = false;
|
||||
|
||||
// Preview LODs mode widget
|
||||
var PreviewLODsMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
// Preview LOD mode widget
|
||||
var previewLODsMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
_previewLODsWidgetButtonMenu = new ContextMenu();
|
||||
_previewLODsWidgetButtonMenu.VisibleChanged += control =>
|
||||
{
|
||||
@@ -248,14 +248,32 @@ namespace FlaxEditor.Viewport.Previews
|
||||
new ViewportWidgetButton("Preview LOD", SpriteHandle.Invalid, _previewLODsWidgetButtonMenu)
|
||||
{
|
||||
TooltipText = "Preview LOD properties",
|
||||
Parent = PreviewLODsMode,
|
||||
Parent = previewLODsMode,
|
||||
};
|
||||
PreviewLODsMode.Parent = this;
|
||||
previewLODsMode.Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AdjustCamera(AssetPreview preview, BoundingBox box)
|
||||
{
|
||||
if (box.Size.MaxValue < preview.NearPlane)
|
||||
{
|
||||
// Very small object
|
||||
preview.NearPlane = Mathf.Min(preview.NearPlane, (float)box.Size.MaxValue * 0.5f + 0.01f);
|
||||
preview.FarPlane *= 0.5f;
|
||||
preview.MovementSpeed = 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
{
|
||||
if (_autoAdjustCamera && Model && Model.IsLoaded)
|
||||
{
|
||||
// Control camera's near/far planes to properly cover object
|
||||
_autoAdjustCamera = false;
|
||||
AdjustCamera(this, Model.GetBox());
|
||||
}
|
||||
|
||||
if (!ScaleToFit)
|
||||
{
|
||||
_previewModel.Scale = Float3.One;
|
||||
|
||||
@@ -51,8 +51,8 @@ namespace FlaxEditor.Viewport.Previews
|
||||
_showCurrentLODButton.IndexInParent = 2;
|
||||
_showCurrentLODButton.CloseMenuOnClick = false;
|
||||
|
||||
// PreviewLODS mode widget
|
||||
var PreviewLODSMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
// Preview LOD mode widget
|
||||
var previewLODMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
_previewLODsWidgetButtonMenu = new ContextMenu();
|
||||
_previewLODsWidgetButtonMenu.VisibleChanged += control =>
|
||||
{
|
||||
@@ -78,9 +78,9 @@ namespace FlaxEditor.Viewport.Previews
|
||||
new ViewportWidgetButton("Preview LOD", SpriteHandle.Invalid, _previewLODsWidgetButtonMenu)
|
||||
{
|
||||
TooltipText = "Preview LOD properties",
|
||||
Parent = PreviewLODSMode,
|
||||
Parent = previewLODMode,
|
||||
};
|
||||
PreviewLODSMode.Parent = this;
|
||||
previewLODMode.Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -509,10 +509,25 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Check if don't have valid behavior picked
|
||||
if (!_behaviorPicker.Value)
|
||||
{
|
||||
// Try to reassign the debug behavior
|
||||
var id = _cachedBehaviorId;
|
||||
if (id == Guid.Empty && Editor.IsPlayMode && Editor.Options.Options.General.AutoAttachDebugPreviewActor)
|
||||
{
|
||||
// Auto-attach preview
|
||||
var behaviorTree = Asset;
|
||||
var behaviors = Level.GetScripts<Behavior>();
|
||||
foreach (var behavior in behaviors)
|
||||
{
|
||||
if (behavior.Tree == behaviorTree &&
|
||||
behavior.IsEnabledInHierarchy)
|
||||
{
|
||||
id = behavior.ID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (id != Guid.Empty)
|
||||
{
|
||||
// Try to reassign the debug behavior
|
||||
var obj = FlaxEngine.Object.TryFind<Behavior>(ref id);
|
||||
if (obj && obj.Tree == Asset)
|
||||
_behaviorPicker.Value = obj;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
[EditorOrder(20), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
|
||||
public MaterialShadingModel ShadingModel;
|
||||
|
||||
[EditorOrder(30), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
|
||||
[EditorOrder(30), VisibleIf(nameof(ShowBlendMode)), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
|
||||
public MaterialBlendMode BlendMode;
|
||||
|
||||
// Rendering
|
||||
@@ -145,6 +145,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
// Visibility conditionals
|
||||
|
||||
private bool ShowBlendMode => Domain != MaterialDomain.Terrain;
|
||||
private bool IsPostProcess => Domain == MaterialDomain.PostProcess;
|
||||
private bool IsDecal => Domain == MaterialDomain.Decal;
|
||||
private bool IsGUI => Domain == MaterialDomain.GUI;
|
||||
@@ -187,6 +188,14 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
// Link
|
||||
Window = window;
|
||||
|
||||
// [Deprecated in 1.12]
|
||||
// Fix old terrain materials to go back into opaque
|
||||
if (Domain == MaterialDomain.Terrain && BlendMode != MaterialBlendMode.Opaque)
|
||||
{
|
||||
BlendMode = MaterialBlendMode.Opaque;
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(Window.MarkAsEdited);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -112,12 +112,14 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
Window._isolateIndex = -1;
|
||||
Window._highlightIndex = -1;
|
||||
Window._meshProxy = this;
|
||||
}
|
||||
|
||||
public override void OnClean()
|
||||
{
|
||||
Window._isolateIndex = -1;
|
||||
Window._highlightIndex = -1;
|
||||
Window._meshProxy = null;
|
||||
|
||||
base.OnClean();
|
||||
}
|
||||
@@ -402,40 +404,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
slots[i].ShadowsMode = shadowsModes[i];
|
||||
}
|
||||
|
||||
UpdateMaterialSlotsUI();
|
||||
Window?._meshProxy?.UpdateMaterialSlotsUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ComboBox> _materialSlotComboBoxes = new List<ComboBox>();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the material slots UI parts. Should be called after material slot rename.
|
||||
/// </summary>
|
||||
public void UpdateMaterialSlotsUI()
|
||||
{
|
||||
Window._skipEffectsGuiEvents = true;
|
||||
|
||||
// Generate material slots labels (with index prefix)
|
||||
var slots = Asset.MaterialSlots;
|
||||
var slotsLabels = new string[slots.Length];
|
||||
for (int i = 0; i < slots.Length; i++)
|
||||
{
|
||||
slotsLabels[i] = string.Format("[{0}] {1}", i, slots[i].Name);
|
||||
}
|
||||
|
||||
// Update comboboxes
|
||||
for (int i = 0; i < _materialSlotComboBoxes.Count; i++)
|
||||
{
|
||||
var comboBox = _materialSlotComboBoxes[i];
|
||||
comboBox.SetItems(slotsLabels);
|
||||
comboBox.SelectedIndex = ((Mesh)comboBox.Tag).MaterialSlotIndex;
|
||||
}
|
||||
|
||||
Window._skipEffectsGuiEvents = false;
|
||||
}
|
||||
|
||||
protected class ProxyEditor : ProxyEditorBase
|
||||
{
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -776,6 +750,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
protected readonly Tabs _tabs;
|
||||
protected readonly ToolStripButton _saveButton;
|
||||
|
||||
private MeshesPropertiesProxyBase _meshProxy;
|
||||
protected ModelImportSettings _importSettings = new ModelImportSettings();
|
||||
protected bool _refreshOnLODsLoaded;
|
||||
protected bool _skipEffectsGuiEvents;
|
||||
|
||||
@@ -338,6 +338,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
PlaySimulation = true,
|
||||
Parent = _split2.Panel1
|
||||
};
|
||||
_preview.PreviewActor.ShowDebugDraw = true;
|
||||
_preview.ShowDebugDraw = true;
|
||||
|
||||
// Timeline
|
||||
_timeline = new ParticleSystemTimeline(_preview, _undo)
|
||||
|
||||
@@ -272,7 +272,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
infoLabel.AutoHeight = true;
|
||||
var sourceAssetPicker = setupGroup.AddPropertyItem("Source Asset").Custom<AssetPicker>().CustomControl;
|
||||
sourceAssetPicker.Height = 48;
|
||||
sourceAssetPicker.CheckValid = CheckSourceAssetValid;
|
||||
sourceAssetPicker.Validator.CheckValid = CheckSourceAssetValid;
|
||||
sourceAssetPicker.SelectedItemChanged += () =>
|
||||
{
|
||||
proxy.Setups.Add(sourceAssetPicker.Validator.SelectedAsset, new SetupProxy());
|
||||
|
||||
@@ -67,12 +67,28 @@ namespace FlaxEditor.Windows
|
||||
b = cm.AddButton("Open", () => Open(item));
|
||||
b.Enabled = proxy != null || isFolder;
|
||||
|
||||
if (_view.SelectedCount > 1)
|
||||
b = cm.AddButton("Open (all selected)", () =>
|
||||
if (_showAllContentInTree)
|
||||
{
|
||||
var selection = _tree.Selection;
|
||||
if (selection.Count > 0)
|
||||
{
|
||||
foreach (var e in _view.Selection)
|
||||
Open(e);
|
||||
});
|
||||
b = cm.AddButton("Open (all selected)", () =>
|
||||
{
|
||||
foreach (var e in _tree.Selection)
|
||||
if (e is ContentItemTreeNode contentNode)
|
||||
Open(contentNode.Item);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_view.SelectedCount > 1)
|
||||
b = cm.AddButton("Open (all selected)", () =>
|
||||
{
|
||||
foreach (var e in _view.Selection)
|
||||
Open(e);
|
||||
});
|
||||
}
|
||||
|
||||
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(item.Path)));
|
||||
|
||||
@@ -137,12 +153,21 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
cm.AddButton("Delete", () => Delete(item));
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Duplicate", _view.Duplicate);
|
||||
cm.AddButton("Cut", _view.Cut);
|
||||
cm.AddButton("Copy", _view.Copy);
|
||||
if (_showAllContentInTree)
|
||||
{
|
||||
cm.AddButton("Duplicate", _treeOnlyPanel.Duplicate);
|
||||
cm.AddButton("Cut", _treeOnlyPanel.Cut);
|
||||
cm.AddButton("Copy", _treeOnlyPanel.Copy);
|
||||
}
|
||||
else
|
||||
{
|
||||
cm.AddButton("Duplicate", _view.Duplicate);
|
||||
cm.AddButton("Cut", _view.Cut);
|
||||
cm.AddButton("Copy", _view.Copy);
|
||||
}
|
||||
}
|
||||
|
||||
b = cm.AddButton("Paste", _view.Paste);
|
||||
b = _showAllContentInTree ? cm.AddButton("Paste", _treeOnlyPanel.Paste) : cm.AddButton("Paste", _view.Paste);
|
||||
b.Enabled = _view.CanPaste();
|
||||
|
||||
if (isFolder && folder.Node is MainContentFolderTreeNode)
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace FlaxEditor.Windows
|
||||
private string _workspaceRebuildLocation;
|
||||
private string _lastViewedFolderBeforeReload;
|
||||
private SplitPanel _split;
|
||||
private Panel _treeOnlyPanel;
|
||||
private TreeViewPanel _treeOnlyPanel;
|
||||
private ContainerControl _treePanelRoot;
|
||||
private ContainerControl _treeHeaderPanel;
|
||||
private Panel _contentItemsSearchPanel;
|
||||
@@ -186,7 +186,7 @@ namespace FlaxEditor.Windows
|
||||
};
|
||||
|
||||
// Tree-only panel (used when showing all content in the tree)
|
||||
_treeOnlyPanel = new Panel(ScrollBars.None)
|
||||
_treeOnlyPanel = new TreeViewPanel
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = new Margin(0, 0, _toolStrip.Bottom, 0),
|
||||
@@ -237,6 +237,7 @@ namespace FlaxEditor.Windows
|
||||
Parent = _contentTreePanel,
|
||||
};
|
||||
_tree.SelectedChanged += OnTreeSelectionChanged;
|
||||
_treeOnlyPanel.ContentTree = _tree;
|
||||
|
||||
// Content items searching query input box and filters selector
|
||||
_contentItemsSearchPanel = new Panel
|
||||
@@ -632,11 +633,7 @@ namespace FlaxEditor.Windows
|
||||
var area = node.TextRect;
|
||||
const float minRenameWidth = 220.0f;
|
||||
if (area.Width < minRenameWidth)
|
||||
{
|
||||
float expand = minRenameWidth - area.Width;
|
||||
area.X -= expand * 0.5f;
|
||||
area.Width = minRenameWidth;
|
||||
}
|
||||
area.Y -= 2;
|
||||
area.Height += 4.0f;
|
||||
popup = RenamePopup.Show(node, area, item.ShortName, true);
|
||||
@@ -889,6 +886,7 @@ namespace FlaxEditor.Windows
|
||||
// Refresh this folder now and try to find duplicated item
|
||||
Editor.ContentDatabase.RefreshFolder(item.ParentFolder, true);
|
||||
RefreshView();
|
||||
RefreshTreeItems();
|
||||
var targetItem = item.ParentFolder.FindChild(targetPath);
|
||||
|
||||
// Start renaming it
|
||||
|
||||
@@ -113,8 +113,6 @@ void Behavior::UpdateAsync()
|
||||
{
|
||||
// Reset State
|
||||
_result = BehaviorUpdateResult::Running;
|
||||
_accumulatedTime = 0.0f;
|
||||
_totalTime = 0;
|
||||
}
|
||||
else if (_result != BehaviorUpdateResult::Running)
|
||||
{
|
||||
|
||||
@@ -2660,6 +2660,28 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
value = actor ? actor->GetPerInstanceRandom() : 0.0f;
|
||||
break;
|
||||
}
|
||||
// Instance Transform
|
||||
case 37:
|
||||
{
|
||||
auto* actor = ScriptingObject::Cast<Actor>(context.Data->Object);
|
||||
const auto& transform = actor ? actor->GetTransform() : Transform::Identity;
|
||||
switch (box->ID)
|
||||
{
|
||||
case 0:
|
||||
value = Value(transform);
|
||||
break;
|
||||
case 1:
|
||||
value = transform.Translation;
|
||||
break;
|
||||
case 2:
|
||||
value = transform.Orientation;
|
||||
break;
|
||||
case 3:
|
||||
value = transform.Scale;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -585,7 +585,8 @@ void Asset::startLoading()
|
||||
{
|
||||
PROFILE_MEM(ContentAssets);
|
||||
ASSERT(!IsLoaded());
|
||||
ASSERT(Platform::AtomicRead(&_loadingTask) == 0);
|
||||
auto task = (Task*)Platform::AtomicRead(&_loadingTask);
|
||||
ASSERT(task == nullptr || task->IsFinished() || task->IsCanceled());
|
||||
auto loadingTask = createLoadingTask();
|
||||
ASSERT(loadingTask != nullptr);
|
||||
Platform::AtomicStore(&_loadingTask, (intptr)loadingTask);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "Engine/Graphics/Async/GPUTask.h"
|
||||
#include "Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h"
|
||||
#include "Engine/Graphics/Models/MeshDeformation.h"
|
||||
#include "Engine/Graphics/Models/ModelDraw.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
@@ -136,105 +137,6 @@ void Model::Draw(const RenderContext& renderContext, MaterialBase* material, con
|
||||
LODs[lodIndex].Draw(renderContext, material, world, flags, receiveDecals, DrawPass::Default, 0, sortOrder);
|
||||
}
|
||||
|
||||
template<typename ContextType>
|
||||
FORCE_INLINE void ModelDraw(Model* model, const RenderContext& renderContext, const ContextType& context, const Mesh::DrawInfo& info)
|
||||
{
|
||||
ASSERT(info.Buffer);
|
||||
if (!model->CanBeRendered())
|
||||
return;
|
||||
if (!info.Buffer->IsValidFor(model))
|
||||
info.Buffer->Setup(model);
|
||||
const auto frame = Engine::FrameCount;
|
||||
const auto modelFrame = info.DrawState->PrevFrame + 1;
|
||||
|
||||
// Select a proper LOD index (model may be culled)
|
||||
int32 lodIndex;
|
||||
if (info.ForcedLOD != -1)
|
||||
{
|
||||
lodIndex = info.ForcedLOD;
|
||||
}
|
||||
else
|
||||
{
|
||||
lodIndex = RenderTools::ComputeModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext);
|
||||
if (lodIndex == -1)
|
||||
{
|
||||
// Handling model fade-out transition
|
||||
if (modelFrame == frame && info.DrawState->PrevLOD != -1 && !renderContext.View.IsSingleFrame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->LODTransition = 0;
|
||||
}
|
||||
|
||||
RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition);
|
||||
|
||||
// Check if end transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
lodIndex += info.LODBias + renderContext.View.ModelLODBias;
|
||||
lodIndex = model->ClampLODIndex(lodIndex);
|
||||
|
||||
if (renderContext.View.IsSingleFrame)
|
||||
{
|
||||
}
|
||||
// Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports)
|
||||
else if (modelFrame == frame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->PrevLOD != lodIndex && info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->LODTransition = 0;
|
||||
}
|
||||
|
||||
RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition);
|
||||
|
||||
// Check if end transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
}
|
||||
}
|
||||
// Check if there was a gap between frames in drawing this model instance
|
||||
else if (modelFrame < frame || info.DrawState->PrevLOD == -1)
|
||||
{
|
||||
// Reset state
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
info.DrawState->LODTransition = 255;
|
||||
}
|
||||
|
||||
// Draw
|
||||
if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame)
|
||||
{
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, 0.0f);
|
||||
}
|
||||
else if (info.DrawState->PrevLOD == -1)
|
||||
{
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress);
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
|
||||
{
|
||||
ModelDraw(this, renderContext, renderContext, info);
|
||||
|
||||
@@ -51,13 +51,14 @@ AssetChunksFlag SkeletonMask::getChunksToPreload() const
|
||||
|
||||
const BitArray<>& SkeletonMask::GetNodesMask()
|
||||
{
|
||||
if (_mask.IsEmpty() && Skeleton && !Skeleton->WaitForLoaded())
|
||||
if (!Skeleton || Skeleton->WaitForLoaded())
|
||||
return _mask;
|
||||
ScopeLock lock(Locker);
|
||||
if (_mask.IsEmpty())
|
||||
{
|
||||
_mask.Resize(Skeleton->Skeleton.Nodes.Count());
|
||||
for (int32 i = 0; i < _mask.Count(); i++)
|
||||
{
|
||||
_mask.Set(i, _maskedNodes.Contains(Skeleton->Skeleton.Nodes[i].Name));
|
||||
}
|
||||
}
|
||||
|
||||
return _mask;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Engine/Graphics/Models/Config.h"
|
||||
#include "Engine/Graphics/Models/MeshDeformation.h"
|
||||
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
|
||||
#include "Engine/Graphics/Models/ModelDraw.h"
|
||||
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
@@ -234,113 +235,14 @@ BoundingBox SkinnedModel::GetBox(int32 lodIndex) const
|
||||
return LODs[lodIndex].GetBox();
|
||||
}
|
||||
|
||||
template<typename ContextType>
|
||||
FORCE_INLINE void SkinnedModelDraw(SkinnedModel* model, const RenderContext& renderContext, const ContextType& context, const SkinnedMesh::DrawInfo& info)
|
||||
{
|
||||
ASSERT(info.Buffer);
|
||||
if (!model->CanBeRendered())
|
||||
return;
|
||||
if (!info.Buffer->IsValidFor(model))
|
||||
info.Buffer->Setup(model);
|
||||
const auto frame = Engine::FrameCount;
|
||||
const auto modelFrame = info.DrawState->PrevFrame + 1;
|
||||
|
||||
// Select a proper LOD index (model may be culled)
|
||||
int32 lodIndex;
|
||||
if (info.ForcedLOD != -1)
|
||||
{
|
||||
lodIndex = info.ForcedLOD;
|
||||
}
|
||||
else
|
||||
{
|
||||
lodIndex = RenderTools::ComputeSkinnedModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext);
|
||||
if (lodIndex == -1)
|
||||
{
|
||||
// Handling model fade-out transition
|
||||
if (modelFrame == frame && info.DrawState->PrevLOD != -1 && !renderContext.View.IsSingleFrame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->LODTransition = 0;
|
||||
}
|
||||
|
||||
RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition);
|
||||
|
||||
// Check if end transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
lodIndex += info.LODBias + renderContext.View.ModelLODBias;
|
||||
lodIndex = model->ClampLODIndex(lodIndex);
|
||||
|
||||
if (renderContext.View.IsSingleFrame)
|
||||
{
|
||||
}
|
||||
// Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports)
|
||||
else if (modelFrame == frame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->PrevLOD != lodIndex && info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->LODTransition = 0;
|
||||
}
|
||||
|
||||
RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition);
|
||||
|
||||
// Check if end transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
}
|
||||
}
|
||||
// Check if there was a gap between frames in drawing this model instance
|
||||
else if (modelFrame < frame || info.DrawState->PrevLOD == -1)
|
||||
{
|
||||
// Reset state
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
info.DrawState->LODTransition = 255;
|
||||
}
|
||||
|
||||
// Draw
|
||||
if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame)
|
||||
{
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, 0.0f);
|
||||
}
|
||||
else if (info.DrawState->PrevLOD == -1)
|
||||
{
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress);
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void SkinnedModel::Draw(const RenderContext& renderContext, const SkinnedMesh::DrawInfo& info)
|
||||
{
|
||||
SkinnedModelDraw(this, renderContext, renderContext, info);
|
||||
ModelDraw(this, renderContext, renderContext, info);
|
||||
}
|
||||
|
||||
void SkinnedModel::Draw(const RenderContextBatch& renderContextBatch, const SkinnedMesh::DrawInfo& info)
|
||||
{
|
||||
SkinnedModelDraw(this, renderContextBatch.GetMainContext(), renderContextBatch, info);
|
||||
ModelDraw(this, renderContextBatch.GetMainContext(), renderContextBatch, info);
|
||||
}
|
||||
|
||||
bool SkinnedModel::SetupLODs(const Span<int32>& meshesCountPerLod)
|
||||
|
||||
@@ -157,7 +157,7 @@ int32 Engine::OnInit(const Char* cmdLine)
|
||||
Application::BeforeRun();
|
||||
LOG_FLOOR();
|
||||
LOG_FLUSH();
|
||||
Time::Synchronize();
|
||||
Time::Synchronize(true);
|
||||
EngineImpl::IsReady = true;
|
||||
PROFILE_MEM_END();
|
||||
|
||||
|
||||
@@ -1626,7 +1626,9 @@ namespace FlaxEngine.Interop
|
||||
// We need private types of this assembly too, DefinedTypes contains a lot of types from other assemblies...
|
||||
var types = referencedTypes.Any() ? assembly.DefinedTypes.Where(x => !referencedTypes.Contains(x.FullName)).ToArray() : assembly.DefinedTypes.ToArray();
|
||||
|
||||
#if FLAX_EDITOR
|
||||
Assert.IsTrue(Utils.GetAssemblies().Count(x => x.GetName().Name == "FlaxEngine.CSharp") == 1);
|
||||
#endif
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,10 +75,11 @@ void TimeSettings::Apply()
|
||||
#endif
|
||||
}
|
||||
|
||||
void Time::TickData::Synchronize(float targetFps, double currentTime)
|
||||
void Time::TickData::Synchronize(float targetFps, double currentTime, bool resetTotalTime)
|
||||
{
|
||||
OnReset(targetFps, currentTime);
|
||||
Time = UnscaledTime = TimeSpan::Zero();
|
||||
if (resetTotalTime)
|
||||
Time = UnscaledTime = TimeSpan::Zero();
|
||||
NextBegin = targetFps > ZeroTolerance ? LastBegin + (1.0f / targetFps) : 0.0;
|
||||
}
|
||||
|
||||
@@ -258,13 +259,13 @@ void Time::SetFixedDeltaTime(bool enable, float value)
|
||||
FixedDeltaTimeValue = value;
|
||||
}
|
||||
|
||||
void Time::Synchronize()
|
||||
void Time::Synchronize(bool resetTotalTime)
|
||||
{
|
||||
// Initialize tick data (based on a time settings)
|
||||
const double time = Platform::GetTimeSeconds();
|
||||
Update.Synchronize(UpdateFPS, time);
|
||||
Physics.Synchronize(PhysicsFPS, time);
|
||||
Draw.Synchronize(DrawFPS, time);
|
||||
Update.Synchronize(UpdateFPS, time, resetTotalTime);
|
||||
Physics.Synchronize(PhysicsFPS, time, resetTotalTime);
|
||||
Draw.Synchronize(DrawFPS, time, resetTotalTime);
|
||||
}
|
||||
|
||||
bool Time::OnBeginUpdate(double time)
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
TimeSpan UnscaledTime;
|
||||
|
||||
public:
|
||||
virtual void Synchronize(float targetFps, double currentTime);
|
||||
virtual void Synchronize(float targetFps, double currentTime, bool resetTotalTime);
|
||||
virtual void OnReset(float targetFps, double currentTime);
|
||||
virtual bool OnTickBegin(double time, float targetFps, float maxDeltaTime);
|
||||
virtual void OnTickEnd();
|
||||
@@ -220,7 +220,8 @@ public:
|
||||
/// <summary>
|
||||
/// Synchronizes update, fixed update and draw. Resets any pending deltas for fresh ticking in sync.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void Synchronize();
|
||||
/// <param name="resetTotalTime">True if reset total time, otherwise only delta time and ticking is reset.</param>
|
||||
API_FUNCTION() static void Synchronize(bool resetTotalTime = false);
|
||||
|
||||
private:
|
||||
// Methods used by the Engine class
|
||||
|
||||
@@ -168,9 +168,6 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCal
|
||||
// Draw visible children
|
||||
if (cluster->Children[0])
|
||||
{
|
||||
// Don't store instances in non-leaf nodes
|
||||
ASSERT_LOW_LAYER(cluster->Instances.IsEmpty());
|
||||
|
||||
BoundingBox box;
|
||||
#define DRAW_CLUSTER(idx) \
|
||||
box = cluster->Children[idx]->TotalBounds; \
|
||||
@@ -184,7 +181,7 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCal
|
||||
DRAW_CLUSTER(3);
|
||||
#undef DRAW_CLUSTER
|
||||
}
|
||||
else
|
||||
//else // Minor clusters can be subdivided and contain instances
|
||||
{
|
||||
// Draw visible instances
|
||||
const auto frame = Engine::FrameCount;
|
||||
@@ -311,9 +308,6 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, Mesh::D
|
||||
// Draw visible children
|
||||
if (cluster->Children[0])
|
||||
{
|
||||
// Don't store instances in non-leaf nodes
|
||||
ASSERT_LOW_LAYER(cluster->Instances.IsEmpty());
|
||||
|
||||
BoundingBox box;
|
||||
#define DRAW_CLUSTER(idx) \
|
||||
box = cluster->Children[idx]->TotalBounds; \
|
||||
@@ -327,7 +321,7 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, Mesh::D
|
||||
DRAW_CLUSTER(3);
|
||||
#undef DRAW_CLUSTER
|
||||
}
|
||||
else
|
||||
//else // Minor clusters can be subdivided and contain instances
|
||||
{
|
||||
// Draw visible instances
|
||||
const auto frame = Engine::FrameCount;
|
||||
@@ -390,7 +384,7 @@ void Foliage::DrawClusterGlobalSDF(class GlobalSignDistanceFieldPass* globalSDF,
|
||||
DRAW_CLUSTER(3);
|
||||
#undef DRAW_CLUSTER
|
||||
}
|
||||
else
|
||||
//else // Minor clusters can be subdivided and contain instances
|
||||
{
|
||||
// Draw visible instances
|
||||
for (int32 i = 0; i < cluster->Instances.Count(); i++)
|
||||
@@ -421,7 +415,7 @@ void Foliage::DrawClusterGlobalSA(GlobalSurfaceAtlasPass* globalSA, const Vector
|
||||
DRAW_CLUSTER(3);
|
||||
#undef DRAW_CLUSTER
|
||||
}
|
||||
else
|
||||
//else // Minor clusters can be subdivided and contain instances
|
||||
{
|
||||
// Draw visible instances
|
||||
for (int32 i = 0; i < cluster->Instances.Count(); i++)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Engine/Content/Assets/Material.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/SoftAssetReference.h"
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#include "Engine/Render2D/Render2D.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
@@ -511,7 +512,7 @@ void GPUDevice::DumpResourcesToLog() const
|
||||
true, // CubeTexture
|
||||
true, // VolumeTexture
|
||||
true, // Buffer
|
||||
true, // Shader
|
||||
false, // Shader
|
||||
false, // PipelineState
|
||||
false, // Descriptor
|
||||
false, // Query
|
||||
@@ -522,29 +523,90 @@ void GPUDevice::DumpResourcesToLog() const
|
||||
const auto type = static_cast<GPUResourceType>(typeIndex);
|
||||
const auto printType = printTypes[typeIndex];
|
||||
|
||||
output.AppendFormat(TEXT("Group: {0}s"), ScriptingEnum::ToString(type));
|
||||
output.AppendLine();
|
||||
|
||||
int32 count = 0;
|
||||
// Get resource sof a given type
|
||||
uint64 memUsage = 0;
|
||||
struct Resource
|
||||
{
|
||||
const GPUResource* Object;
|
||||
uint64 MemoryUsage;
|
||||
|
||||
bool operator<(const Resource& other) const
|
||||
{
|
||||
if (MemoryUsage != other.MemoryUsage)
|
||||
return MemoryUsage > other.MemoryUsage;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
return Object->GetName().Compare(other.Object->GetName()) > 0;
|
||||
#else
|
||||
return (uintptr)Object < (uintptr)other.Object;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
Array<Resource> resources;
|
||||
for (int32 i = 0; i < _resources.Count(); i++)
|
||||
{
|
||||
const GPUResource* resource = _resources[i];
|
||||
if (resource->GetResourceType() == type && resource->GetMemoryUsage() != 0)
|
||||
{
|
||||
count++;
|
||||
memUsage += resource->GetMemoryUsage();
|
||||
auto str = resource->ToString();
|
||||
if (str.HasChars() && printType)
|
||||
{
|
||||
output.Append(TEXT('\t'));
|
||||
output.Append(str);
|
||||
output.AppendLine();
|
||||
}
|
||||
resources.Add({ resource, resource->GetMemoryUsage() });
|
||||
}
|
||||
}
|
||||
if (resources.IsEmpty())
|
||||
continue;
|
||||
output.AppendFormat(TEXT("> {0}:"), ScriptingEnum::ToString(type));
|
||||
output.AppendLine();
|
||||
|
||||
output.AppendFormat(TEXT("Total count: {0}, memory usage: {1}"), count, Utilities::BytesToText(memUsage));
|
||||
// Sort them by size
|
||||
Sorting::QuickSort(resources);
|
||||
|
||||
// Print resources
|
||||
for (auto e : resources)
|
||||
{
|
||||
memUsage += e.MemoryUsage;
|
||||
if (!printType)
|
||||
continue;
|
||||
output.Append(TEXT(" "));
|
||||
output.Append(Utilities::BytesToText(e.MemoryUsage));
|
||||
output.Append(TEXT(", "));
|
||||
if (e.Object->Is<GPUTexture>())
|
||||
{
|
||||
auto texture = (GPUTexture*)e.Object;
|
||||
auto& desc = texture->GetDescription();
|
||||
output.AppendFormat(TEXT("Size: {}x{}x{}[{}], "), desc.Width, desc.Height, desc.Depth, desc.ArraySize);
|
||||
if (texture->ResidentMipLevels() == desc.MipLevels)
|
||||
output.AppendFormat(TEXT("Mips: {}, "), desc.MipLevels);
|
||||
else
|
||||
output.AppendFormat(TEXT("Mips: {}/{}, "), texture->ResidentMipLevels(), desc.MipLevels);
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
auto name = texture->GetName();
|
||||
#else
|
||||
StringView name;
|
||||
#endif
|
||||
output.AppendFormat(TEXT("Format: {}, Flags: {}, {}"), ScriptingEnum::ToString(desc.Format), ScriptingEnum::ToStringFlags(desc.Flags), name);
|
||||
}
|
||||
else if (e.Object->Is<GPUBuffer>())
|
||||
{
|
||||
auto buffer = (GPUBuffer*)e.Object;
|
||||
auto& desc = buffer->GetDescription();
|
||||
output.AppendFormat(TEXT("Stride: {} bytes, "), desc.Stride);
|
||||
if (desc.Format != PixelFormat::Unknown)
|
||||
output.AppendFormat(TEXT("Format: {}, "), ScriptingEnum::ToString(desc.Format));
|
||||
if (desc.Usage != GPUResourceUsage::Default)
|
||||
output.AppendFormat(TEXT("Usage: {}, "), ScriptingEnum::ToString(desc.Usage));
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
auto name = buffer->GetName();
|
||||
#else
|
||||
StringView name;
|
||||
#endif
|
||||
output.AppendFormat(TEXT("Flags: {}, {}"), ScriptingEnum::ToStringFlags(desc.Flags), name);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Append(e.Object->ToString());
|
||||
}
|
||||
output.AppendLine();
|
||||
}
|
||||
|
||||
output.AppendFormat(TEXT("Total count: {0}, memory usage: {1}"), resources.Count(), Utilities::BytesToText(memUsage));
|
||||
output.AppendLine();
|
||||
output.AppendLine();
|
||||
}
|
||||
@@ -553,6 +615,12 @@ void GPUDevice::DumpResourcesToLog() const
|
||||
LOG_STR(Info, output.ToStringView());
|
||||
}
|
||||
|
||||
void GPUDevice::DumpResources()
|
||||
{
|
||||
if (GPUDevice::Instance)
|
||||
GPUDevice::Instance->DumpResourcesToLog();
|
||||
}
|
||||
|
||||
extern void ClearVertexLayoutCache();
|
||||
|
||||
void GPUDevice::preDispose()
|
||||
|
||||
@@ -389,6 +389,11 @@ public:
|
||||
/// </summary>
|
||||
void DumpResourcesToLog() const;
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all GPU resources information to the log.
|
||||
/// </summary>
|
||||
API_FUNCTION(Attributes="DebugCommand") static void DumpResources();
|
||||
|
||||
protected:
|
||||
virtual void preDispose();
|
||||
|
||||
|
||||
@@ -97,4 +97,13 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial class MaterialParameter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ParameterType} {Name} = {Value?.ToString() ?? "null"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "MaterialShader.h"
|
||||
#include "Engine/Core/Math/Rectangle.h"
|
||||
#include "Engine/Core/Types/Span.h"
|
||||
#include "Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h"
|
||||
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
|
||||
|
||||
@@ -150,21 +150,6 @@ bool TerrainMaterialShader::Load()
|
||||
}
|
||||
#endif
|
||||
|
||||
// Support blending but then use only emissive channel
|
||||
switch (_info.BlendMode)
|
||||
{
|
||||
case MaterialBlendMode::Transparent:
|
||||
psDesc.BlendMode = BlendingMode::AlphaBlend;
|
||||
break;
|
||||
case MaterialBlendMode::Additive:
|
||||
psDesc.BlendMode = BlendingMode::Additive;
|
||||
break;
|
||||
case MaterialBlendMode::Multiply:
|
||||
psDesc.BlendMode = BlendingMode::Multiply;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
|
||||
// GBuffer Pass
|
||||
psDesc.VS = _shader->GetVS("VS");
|
||||
psDesc.PS = _shader->GetPS("PS_GBuffer");
|
||||
@@ -185,7 +170,6 @@ bool TerrainMaterialShader::Load()
|
||||
|
||||
// Depth Pass
|
||||
psDesc.CullMode = CullMode::TwoSided;
|
||||
psDesc.BlendMode = BlendingMode::Opaque;
|
||||
psDesc.DepthClipEnable = false;
|
||||
psDesc.DepthWriteEnable = true;
|
||||
psDesc.DepthEnable = true;
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/Materials/MaterialInfo.h"
|
||||
|
||||
template<typename ModelType, typename DrawInfoType>
|
||||
FORCE_INLINE bool ModelDrawTransition(ModelType* model, const DrawInfoType& info)
|
||||
{
|
||||
for (auto& e : *info.Buffer)
|
||||
{
|
||||
if (e.Material && EnumHasAllFlags(e.Material->GetInfo().FeaturesFlags, MaterialFeaturesFlags::DitheredLODTransition))
|
||||
return true;
|
||||
}
|
||||
for (auto& e : model->MaterialSlots)
|
||||
{
|
||||
if (e.Material && EnumHasAllFlags(e.Material->GetInfo().FeaturesFlags, MaterialFeaturesFlags::DitheredLODTransition))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename ModelType, typename DrawInfoType, typename ContextType>
|
||||
FORCE_INLINE void ModelDraw(ModelType* model, const RenderContext& renderContext, const ContextType& context, const DrawInfoType& info)
|
||||
{
|
||||
ASSERT(info.Buffer);
|
||||
if (!model->CanBeRendered())
|
||||
return;
|
||||
if (!info.Buffer->IsValidFor(model))
|
||||
info.Buffer->Setup(model);
|
||||
const auto frame = Engine::FrameCount;
|
||||
const auto modelFrame = info.DrawState->PrevFrame + 1;
|
||||
|
||||
// Select a proper LOD index (model may be culled)
|
||||
int32 lodIndex;
|
||||
if (info.ForcedLOD != -1)
|
||||
{
|
||||
lodIndex = info.ForcedLOD;
|
||||
}
|
||||
else
|
||||
{
|
||||
lodIndex = RenderTools::ComputeModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext);
|
||||
if (lodIndex == -1)
|
||||
{
|
||||
// Handling model fade-out transition
|
||||
if (modelFrame == frame && info.DrawState->PrevLOD != -1 && !renderContext.View.IsSingleFrame && ModelDrawTransition(model, info))
|
||||
{
|
||||
// Check if start transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->LODTransition = 0;
|
||||
}
|
||||
|
||||
RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition);
|
||||
|
||||
// Check if end transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
lodIndex += info.LODBias + renderContext.View.ModelLODBias;
|
||||
lodIndex = model->ClampLODIndex(lodIndex);
|
||||
|
||||
if (renderContext.View.IsSingleFrame)
|
||||
{
|
||||
}
|
||||
// Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports)
|
||||
else if (modelFrame == frame)
|
||||
{
|
||||
// Check if materials use transition
|
||||
if (!ModelDrawTransition(model, info))
|
||||
{
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
info.DrawState->LODTransition = 255;
|
||||
}
|
||||
|
||||
// Check if start transition
|
||||
if (info.DrawState->PrevLOD != lodIndex && info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->LODTransition = 0;
|
||||
}
|
||||
|
||||
RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition);
|
||||
|
||||
// Check if end transition
|
||||
if (info.DrawState->LODTransition == 255)
|
||||
{
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
}
|
||||
}
|
||||
// Check if there was a gap between frames in drawing this model instance
|
||||
else if (modelFrame < frame || info.DrawState->PrevLOD == -1)
|
||||
{
|
||||
// Reset state
|
||||
info.DrawState->PrevLOD = lodIndex;
|
||||
info.DrawState->LODTransition = 255;
|
||||
}
|
||||
|
||||
// Draw
|
||||
if (info.DrawState->PrevLOD == lodIndex || info.DrawState->LODTransition == 255 || renderContext.View.IsSingleFrame)
|
||||
{
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, 0.0f);
|
||||
}
|
||||
else if (info.DrawState->PrevLOD == -1)
|
||||
{
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
|
||||
model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress);
|
||||
model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f);
|
||||
}
|
||||
}
|
||||
@@ -461,7 +461,7 @@ int32 RenderTools::ComputeModelLOD(const Model* model, const Float3& origin, flo
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 RenderTools::ComputeSkinnedModelLOD(const SkinnedModel* model, const Float3& origin, float radius, const RenderContext& renderContext)
|
||||
int32 RenderTools::ComputeModelLOD(const SkinnedModel* model, const Float3& origin, float radius, const RenderContext& renderContext)
|
||||
{
|
||||
const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View);
|
||||
const float screenRadiusSquared = ComputeBoundsScreenRadiusSquared(origin, radius, *lodView) * renderContext.View.ModelLODDistanceFactorSqrt;
|
||||
@@ -486,6 +486,11 @@ int32 RenderTools::ComputeSkinnedModelLOD(const SkinnedModel* model, const Float
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 RenderTools::ComputeSkinnedModelLOD(const SkinnedModel* model, const Float3& origin, float radius, const RenderContext& renderContext)
|
||||
{
|
||||
return ComputeModelLOD(model, origin, radius, renderContext);
|
||||
}
|
||||
|
||||
void RenderTools::ComputeCascadeUpdateFrequency(int32 cascadeIndex, int32 cascadeCount, int32& updateFrequency, int32& updatePhrase, int32 updateMaxCountPerFrame)
|
||||
{
|
||||
switch (updateMaxCountPerFrame)
|
||||
|
||||
@@ -109,15 +109,26 @@ public:
|
||||
/// <returns>The zero-based LOD index. Returns -1 if model should not be rendered.</returns>
|
||||
API_FUNCTION() static int32 ComputeModelLOD(const Model* model, API_PARAM(Ref) const Float3& origin, float radius, API_PARAM(Ref) const RenderContext& renderContext);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the model LOD index to use during rendering.
|
||||
/// </summary>
|
||||
/// <param name="model">The model.</param>
|
||||
/// <param name="origin">The bounds origin.</param>
|
||||
/// <param name="radius">The bounds radius.</param>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <returns>The zero-based LOD index. Returns -1 if model should not be rendered.</returns>
|
||||
API_FUNCTION() static int32 ComputeModelLOD(const SkinnedModel* model, API_PARAM(Ref) const Float3& origin, float radius, API_PARAM(Ref) const RenderContext& renderContext);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the skinned model LOD index to use during rendering.
|
||||
/// [Deprecated in v1.12]
|
||||
/// </summary>
|
||||
/// <param name="model">The skinned model.</param>
|
||||
/// <param name="origin">The bounds origin.</param>
|
||||
/// <param name="radius">The bounds radius.</param>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <returns>The zero-based LOD index. Returns -1 if model should not be rendered.</returns>
|
||||
API_FUNCTION() static int32 ComputeSkinnedModelLOD(const SkinnedModel* model, API_PARAM(Ref) const Float3& origin, float radius, API_PARAM(Ref) const RenderContext& renderContext);
|
||||
API_FUNCTION() DEPRECATED("Use ComputeModelLOD instead.") static int32 ComputeSkinnedModelLOD(const SkinnedModel* model, API_PARAM(Ref) const Float3& origin, float radius, API_PARAM(Ref) const RenderContext& renderContext);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the sorting key for depth value (quantized)
|
||||
|
||||
@@ -302,6 +302,12 @@ bool TextureBase::HasStreamingError() const
|
||||
return _texture.Streaming.Error;
|
||||
}
|
||||
|
||||
void TextureBase::SetStreamingVisible() const
|
||||
{
|
||||
if (_texture.GetTexture())
|
||||
_texture.GetTexture()->LastRenderTime = Platform::GetTimeSeconds();
|
||||
}
|
||||
|
||||
BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& slicePitch)
|
||||
{
|
||||
BytesContainer result;
|
||||
|
||||
@@ -153,6 +153,11 @@ public:
|
||||
/// </summary>
|
||||
API_PROPERTY() bool HasStreamingError() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the texture as visible this frame to inform streaming about usage which will stream boost its priority for the streaming.
|
||||
/// </summary>
|
||||
API_FUNCTION() void SetStreamingVisible() const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the mip data.
|
||||
|
||||
@@ -2257,6 +2257,7 @@ bool FenceManagerVulkan::WaitForFence(FenceVulkan* fence, float timeoutSeconds)
|
||||
fence->IsSignaled = true;
|
||||
return false;
|
||||
}
|
||||
LOG(Warning, "vkWaitForFences failed with timeout: {}s", timeoutSeconds);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -321,17 +321,19 @@ bool GPUDeviceWebGPU::Init()
|
||||
FormatSupport::Buffer |
|
||||
FormatSupport::InputAssemblyIndexBuffer |
|
||||
FormatSupport::InputAssemblyVertexBuffer;
|
||||
auto supportsTexture =
|
||||
auto supportsTextureUnfilterable =
|
||||
FormatSupport::Texture1D |
|
||||
FormatSupport::Texture2D |
|
||||
FormatSupport::Texture3D |
|
||||
FormatSupport::TextureCube |
|
||||
FormatSupport::ShaderLoad |
|
||||
FormatSupport::Mip;
|
||||
auto supportsTexture =
|
||||
supportsTextureUnfilterable |
|
||||
FormatSupport::ShaderSample |
|
||||
FormatSupport::ShaderSampleComparison |
|
||||
FormatSupport::ShaderGather |
|
||||
FormatSupport::ShaderGatherComparison |
|
||||
FormatSupport::Mip;
|
||||
FormatSupport::ShaderGatherComparison;
|
||||
auto supportsRender =
|
||||
FormatSupport::RenderTarget |
|
||||
FormatSupport::Blendable;
|
||||
@@ -414,18 +416,18 @@ bool GPUDeviceWebGPU::Init()
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_SNorm].Support |= supportsBuffer | supportsTexture | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_UInt].Support |= supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_SInt].Support |= supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_SNorm].Support |= supportsBuffer | supportsTexture | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_UNorm].Support |= supportsBuffer | supportsTextureUnfilterable | supportsRender | supportsMSAA | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_SNorm].Support |= supportsBuffer | supportsTextureUnfilterable | supportsBasicStorage;
|
||||
//FeaturesPerFormat[(int32)PixelFormat::R16_UInt].Support |= supportsBasicStorage; // TODO: fix issues with particle indices buffer that could use it
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_SInt].Support |= supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_Float].Support |= supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMultisampling | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_SNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMultisampling | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_UNorm].Support |= supportsBuffer | supportsTextureUnfilterable | supportsRender | supportsMultisampling | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_SNorm].Support |= supportsBuffer | supportsTextureUnfilterable | supportsRender | supportsMultisampling | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_UInt].Support |= supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_SInt].Support |= supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_Float].Support |= supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMultisampling | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_SNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMultisampling | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_UNorm].Support |= supportsBuffer | supportsTextureUnfilterable | supportsRender | supportsMultisampling | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_SNorm].Support |= supportsBuffer | supportsTextureUnfilterable | supportsRender | supportsMultisampling | supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R10G10B10A2_UNorm].Support |= supportsBasicStorage;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R11G11B10_Float].Support |= supportsBasicStorage;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Flax.Build.NativeCpp;
|
||||
using Flax.Build.Platforms;
|
||||
@@ -23,6 +24,9 @@ public class GraphicsDeviceWebGPU : GraphicsDeviceBaseModule
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
if (!EmscriptenSdk.Instance.IsValid)
|
||||
throw new Exception("Cannot build WebGPU for Web without Emscripten SDK. Check environment variable 'EMSDK'.");
|
||||
|
||||
var port = "--use-port=emdawnwebgpu:cpp_bindings=false";
|
||||
options.OutputFiles.Add(port);
|
||||
options.CompileEnv.CustomArgs.Add(port);
|
||||
|
||||
@@ -1332,6 +1332,11 @@ MaterialBase* AnimatedModel::GetMaterial(int32 entryIndex)
|
||||
return material;
|
||||
}
|
||||
|
||||
ModelBase* AnimatedModel::GetModel()
|
||||
{
|
||||
return SkinnedModel.Get();
|
||||
}
|
||||
|
||||
bool AnimatedModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
|
||||
{
|
||||
auto model = SkinnedModel.Get();
|
||||
|
||||
@@ -476,6 +476,7 @@ public:
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
const Span<MaterialSlot> GetMaterialSlots() const override;
|
||||
MaterialBase* GetMaterial(int32 entryIndex) override;
|
||||
ModelBase* GetModel() override;
|
||||
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
|
||||
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
|
||||
bool GetMeshData(const MeshReference& ref, MeshBufferType type, BytesContainer& result, int32& count, GPUVertexLayout** layout) const override;
|
||||
|
||||
@@ -66,6 +66,11 @@ public:
|
||||
/// <param name="entryIndex">The material slot entry index.</param>
|
||||
API_FUNCTION(Sealed) virtual MaterialBase* GetMaterial(int32 entryIndex) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model (base class).
|
||||
/// </summary>
|
||||
API_FUNCTION(Sealed) virtual ModelBase* GetModel() = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the material to the entry slot. Can be used to override the material of the meshes using this slot.
|
||||
/// </summary>
|
||||
|
||||
@@ -367,6 +367,11 @@ MaterialBase* SplineModel::GetMaterial(int32 entryIndex)
|
||||
return material;
|
||||
}
|
||||
|
||||
ModelBase* SplineModel::GetModel()
|
||||
{
|
||||
return Model.Get();
|
||||
}
|
||||
|
||||
void SplineModel::UpdateBounds()
|
||||
{
|
||||
OnSplineUpdated();
|
||||
|
||||
@@ -117,6 +117,7 @@ public:
|
||||
void OnParentChanged() override;
|
||||
const Span<MaterialSlot> GetMaterialSlots() const override;
|
||||
MaterialBase* GetMaterial(int32 entryIndex) override;
|
||||
ModelBase* GetModel() override;
|
||||
void UpdateBounds() override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -599,6 +599,11 @@ MaterialBase* StaticModel::GetMaterial(int32 entryIndex)
|
||||
return material;
|
||||
}
|
||||
|
||||
ModelBase* StaticModel::GetModel()
|
||||
{
|
||||
return Model.Get();
|
||||
}
|
||||
|
||||
bool StaticModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
|
||||
{
|
||||
auto model = Model.Get();
|
||||
|
||||
@@ -178,6 +178,7 @@ public:
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
const Span<MaterialSlot> GetMaterialSlots() const override;
|
||||
MaterialBase* GetMaterial(int32 entryIndex) override;
|
||||
ModelBase* GetModel() override;
|
||||
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
|
||||
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
|
||||
bool GetMeshData(const MeshReference& ref, MeshBufferType type, BytesContainer& result, int32& count, GPUVertexLayout** layout) const override;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
||||
#include "Engine/Utilities/Noise.h"
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
#include "Engine/Engine/Units.h"
|
||||
|
||||
// ReSharper disable CppCStyleCast
|
||||
// ReSharper disable CppClangTidyClangDiagnosticCastAlign
|
||||
@@ -1485,7 +1486,9 @@ void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNod
|
||||
if (node->UsePerParticleDataResolve())
|
||||
return;
|
||||
|
||||
const Color color = Color::White;
|
||||
const Color colorPosition = Color::White;
|
||||
const Color colorCollision = Color::GreenYellow;
|
||||
const Color colorCollisionFill = colorCollision.AlphaMultiplied(0.1f);
|
||||
switch (node->TypeID)
|
||||
{
|
||||
case 202: // Position (sphere surface)
|
||||
@@ -1493,7 +1496,7 @@ void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNod
|
||||
{
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
const float radius = (float)GetValue(node->GetBox(1), 3);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, radius), color, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, radius), colorPosition, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
case 203: // Position (plane)
|
||||
@@ -1503,7 +1506,7 @@ void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNod
|
||||
const Float3 halfExtent = Float3(size.X * 0.5f, 0.0f, size.Y * 0.5f);
|
||||
OrientedBoundingBox box(halfExtent, Transform(center));
|
||||
box.Transform(transform);
|
||||
DEBUG_DRAW_WIRE_BOX(box, color, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_BOX(box, colorPosition, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
case 204: // Position (circle)
|
||||
@@ -1511,7 +1514,7 @@ void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNod
|
||||
{
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
const float radius = (float)GetValue(node->GetBox(1), 3);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(center, transform.Orientation * Quaternion::Euler(90, 0, 0), radius, 0.0f, color, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(center, transform.Orientation * Quaternion::Euler(90, 0, 0), radius, 0.0f, colorPosition, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
case 206: // Position (box surface)
|
||||
@@ -1521,7 +1524,7 @@ void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNod
|
||||
const Float3 size = (Float3)GetValue(node->GetBox(1), 3);
|
||||
OrientedBoundingBox box(size * 0.5f, Transform(center));
|
||||
box.Transform(transform);
|
||||
DEBUG_DRAW_WIRE_BOX(box, color, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_BOX(box, colorPosition, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Position (cylinder)
|
||||
@@ -1530,7 +1533,7 @@ void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNod
|
||||
const float height = (float)GetValue(node->GetBox(2), 4);
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2) + Float3(0, 0, height * 0.5f));
|
||||
const float radius = (float)GetValue(node->GetBox(1), 3);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(center, transform.Orientation * Quaternion::Euler(90, 0, 0), radius, height, color, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(center, transform.Orientation * Quaternion::Euler(90, 0, 0), radius, height, colorPosition, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Position (line)
|
||||
@@ -1538,7 +1541,7 @@ void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNod
|
||||
{
|
||||
const Float3 start = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
const Float3 end = transform.LocalToWorld((Float3)GetValue(node->GetBox(1), 3));
|
||||
DEBUG_DRAW_LINE(start, end, color, 0.0f, true);
|
||||
DEBUG_DRAW_LINE(start, end, colorPosition, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Position (torus)
|
||||
@@ -1547,15 +1550,54 @@ void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNod
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
const float radius = Math::Max((float)GetValue(node->GetBox(1), 3), ZeroTolerance);
|
||||
const float thickness = (float)GetValue(node->GetBox(2), 4);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, radius + thickness), color, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, radius + thickness), colorPosition, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Position (spiral)
|
||||
case 214:
|
||||
{
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, 5.0f), color, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, 5.0f), colorPosition, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Collision (plane)
|
||||
case 330:
|
||||
{
|
||||
const Float3 position = transform.LocalToWorld((Float3)GetValue(node->GetBox(5), 8));
|
||||
//const Float3 normal = transform.Orientation * (Float3)GetValue(node->GetBox(6), 9) * ((bool)node->Values[2] ? -1.0f : 1.0f);
|
||||
const Float3 extent(METERS_TO_UNITS(10), 0.001f, METERS_TO_UNITS(10));
|
||||
DEBUG_DRAW_BOX(BoundingBox(position - extent, position + extent), colorCollisionFill, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_BOX(BoundingBox(position - extent, position + extent), colorCollision, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Collision (sphere)
|
||||
case 331:
|
||||
{
|
||||
const Float3 position = transform.LocalToWorld((Float3)GetValue(node->GetBox(5), 8));
|
||||
const float radius = (float)GetValue(node->GetBox(6), 9);
|
||||
DEBUG_DRAW_SPHERE(BoundingSphere(position, radius), colorCollisionFill, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(position, radius), colorCollision, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Collision (box)
|
||||
case 332:
|
||||
{
|
||||
const Float3 center = (Float3)GetValue(node->GetBox(5), 8);
|
||||
const Float3 size = (Float3)GetValue(node->GetBox(6), 9);
|
||||
OrientedBoundingBox box(size * 0.5f, Transform(center));
|
||||
box.Transform(transform);
|
||||
DEBUG_DRAW_BOX(box, colorCollisionFill, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_BOX(box, colorCollision, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Collision (cylinder)
|
||||
case 333:
|
||||
{
|
||||
const float height = (float)GetValue(node->GetBox(6), 9);
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(5), 8) + Float3(0, 0, height * 0.5f));
|
||||
const float radius = (float)GetValue(node->GetBox(7), 10);
|
||||
DEBUG_DRAW_CYLINDER(center, transform.Orientation * Quaternion::Euler(90, 0, 0), radius, height, colorCollisionFill, 0.0f, true);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(center, transform.Orientation * Quaternion::Euler(90, 0, 0), radius, height, colorCollision, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,6 +437,8 @@ void ParticleEmitterGraphCPUExecutor::DrawDebug(ParticleEmitter* emitter, Partic
|
||||
DebugDrawModule(module, transform);
|
||||
for (auto module : emitter->Graph.InitModules)
|
||||
DebugDrawModule(module, transform);
|
||||
for (auto module : emitter->Graph.UpdateModules)
|
||||
DebugDrawModule(module, transform);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -52,6 +52,10 @@ void Font::GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback)
|
||||
for (int32 fallbackIndex = 0; fallbackIndex < FallbackFonts.Count(); fallbackIndex++)
|
||||
{
|
||||
FontAsset* fallbackFont = FallbackFonts.Get()[fallbackIndex].Get();
|
||||
if (fallbackFont && _asset->GetOptions().RasterMode == FontRasterMode::MSDF)
|
||||
{
|
||||
fallbackFont = fallbackFont->GetMSDF();
|
||||
}
|
||||
if (fallbackFont && fallbackFont->ContainsChar(c))
|
||||
{
|
||||
fallbackFont->CreateFont(GetSize())->GetCharacter(c, result, enableFallback);
|
||||
|
||||
@@ -69,6 +69,7 @@ void FontAsset::unload(bool isReloading)
|
||||
_fontFile.Release();
|
||||
_virtualBold = nullptr;
|
||||
_virtualItalic = nullptr;
|
||||
_virtualMSDF = nullptr;
|
||||
}
|
||||
|
||||
AssetChunksFlag FontAsset::getChunksToPreload() const
|
||||
@@ -155,6 +156,22 @@ FontAsset* FontAsset::GetItalic()
|
||||
return _virtualItalic;
|
||||
}
|
||||
|
||||
FontAsset* FontAsset::GetMSDF()
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
if (_options.RasterMode == FontRasterMode::MSDF)
|
||||
return this;
|
||||
if (!_virtualMSDF)
|
||||
{
|
||||
_virtualMSDF = Content::CreateVirtualAsset<FontAsset>();
|
||||
_virtualMSDF->Init(_fontFile);
|
||||
auto options = _options;
|
||||
options.RasterMode = FontRasterMode::MSDF;
|
||||
_virtualMSDF->SetOptions(options);
|
||||
}
|
||||
return _virtualMSDF;
|
||||
}
|
||||
|
||||
bool FontAsset::Init(const BytesContainer& fontFile)
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
@@ -122,6 +122,7 @@ private:
|
||||
Array<Font*, InlinedAllocation<32>> _fonts;
|
||||
AssetReference<FontAsset> _virtualBold;
|
||||
AssetReference<FontAsset> _virtualItalic;
|
||||
AssetReference<FontAsset> _virtualMSDF;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -180,6 +181,12 @@ public:
|
||||
/// <returns>The virtual font or this.</returns>
|
||||
API_FUNCTION() FontAsset* GetItalic();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MSDF version of the font. Returns itself or creates a new virtual font asset using this font but rasterized with Multi-channel Signed Distance Field (MSDF).
|
||||
/// </summary>
|
||||
/// <returns>The virtual font or this.</returns>
|
||||
API_FUNCTION() FontAsset* GetMSDF();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the font with a custom font file data.
|
||||
/// </summary>
|
||||
|
||||
@@ -98,7 +98,7 @@ bool AmbientOcclusionPass::Init()
|
||||
_psNonSmartBlur = GPUDevice::Instance->CreatePipelineState();
|
||||
_psApply = GPUDevice::Instance->CreatePipelineState();
|
||||
_psApplyHalf = GPUDevice::Instance->CreatePipelineState();
|
||||
_depthBounds = GPUDevice::Instance->Limits.HasDepthBounds;
|
||||
_depthBounds = GPUDevice::Instance->Limits.HasDepthBounds && GPUDevice::Instance->Limits.HasReadOnlyDepth;
|
||||
|
||||
// Load shader
|
||||
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/SSAO"));
|
||||
|
||||
@@ -546,7 +546,10 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input,
|
||||
// - 5 - LensStar - lens star texture
|
||||
// - 7 - ColorGradingLUT
|
||||
context->BindSR(0, input->View());
|
||||
context->BindSR(4, GetCustomOrDefault(settings.LensFlares.LensDirt, _defaultLensDirt, TEXT("Engine/Textures/DefaultLensDirt")));
|
||||
if ((useLensFlares || useBloom) && settings.LensFlares.LensDirtIntensity > 0)
|
||||
context->BindSR(4, GetCustomOrDefault(settings.LensFlares.LensDirt, _defaultLensDirt, TEXT("Engine/Textures/DefaultLensDirt")));
|
||||
else
|
||||
context->BindSR(4, device->GetDefaultBlackTexture());
|
||||
context->BindSR(7, colorGradingLutView);
|
||||
|
||||
// Composite final frame during single pass (done in full resolution)
|
||||
|
||||
@@ -38,6 +38,7 @@ private:
|
||||
mutable int32 _hasCachedClasses : 1;
|
||||
|
||||
mutable ClassesDictionary _classes;
|
||||
mutable ClassesDictionary _typeClasses;
|
||||
|
||||
int32 _reloadCount;
|
||||
StringAnsi _name;
|
||||
@@ -234,6 +235,11 @@ public:
|
||||
/// </summary>
|
||||
const ClassesDictionary& GetClasses() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the classes lookup cache that includes runtime-cached types. Non-stable to iterate over due to dynamic types caching.
|
||||
/// </summary>
|
||||
ClassesDictionary& GetTypeClasses() const;
|
||||
|
||||
private:
|
||||
bool LoadCorlib();
|
||||
bool LoadImage(const String& assemblyPath, const StringView& nativePath);
|
||||
|
||||
@@ -142,7 +142,8 @@ void MAssembly::Unload(bool isReloading)
|
||||
_isLoaded = false;
|
||||
_hasCachedClasses = false;
|
||||
#if USE_NETCORE
|
||||
ArenaAllocator::ClearDelete(_classes);
|
||||
ArenaAllocator::ClearDelete(_typeClasses);
|
||||
_classes.Clear();
|
||||
#else
|
||||
_classes.ClearDelete();
|
||||
#endif
|
||||
|
||||
@@ -355,7 +355,7 @@ void MCore::UnloadScriptingAssemblyLoadContext()
|
||||
MAssembly* a = e.Value;
|
||||
if (!a->IsLoaded() || !a->_hasCachedClasses)
|
||||
continue;
|
||||
for (const auto& q : a->GetClasses())
|
||||
for (const auto& q : a->GetTypeClasses())
|
||||
{
|
||||
MClass* c = q.Value;
|
||||
c->_hasCachedAttributes = false;
|
||||
@@ -781,6 +781,7 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
|
||||
MCore::GC::FreeMemory((void*)managedClasses[i].fullname);
|
||||
MCore::GC::FreeMemory((void*)managedClasses[i].namespace_);
|
||||
}
|
||||
_typeClasses = _classes;
|
||||
|
||||
static void* RegisterManagedClassNativePointersPtr = GetStaticMethodPointer(TEXT("RegisterManagedClassNativePointers"));
|
||||
CallStaticMethod<void, NativeClassDefinitions**, int>(RegisterManagedClassNativePointersPtr, &managedClasses, classCount);
|
||||
@@ -799,6 +800,12 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
|
||||
return _classes;
|
||||
}
|
||||
|
||||
MAssembly::ClassesDictionary& MAssembly::GetTypeClasses() const
|
||||
{
|
||||
GetClasses();
|
||||
return _typeClasses;
|
||||
}
|
||||
|
||||
void GetAssemblyName(void* assemblyHandle, StringAnsi& name, StringAnsi& fullname)
|
||||
{
|
||||
static void* GetAssemblyNamePtr = GetStaticMethodPointer(TEXT("GetAssemblyName"));
|
||||
@@ -828,7 +835,7 @@ DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* man
|
||||
MClass* klass = assembly->Memory.New<MClass>(assembly, managedClass->typeHandle, managedClass->name, managedClass->fullname, managedClass->namespace_, managedClass->typeAttributes);
|
||||
if (assembly != nullptr)
|
||||
{
|
||||
auto& classes = const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses());
|
||||
auto& classes = assembly->GetTypeClasses();
|
||||
MClass* oldKlass;
|
||||
if (classes.TryGet(klass->GetFullName(), oldKlass))
|
||||
{
|
||||
@@ -1746,7 +1753,7 @@ MClass* GetOrCreateClass(MType* typeHandle)
|
||||
klass = assembly->Memory.New<MClass>(assembly, classInfo.typeHandle, classInfo.name, classInfo.fullname, classInfo.namespace_, classInfo.typeAttributes);
|
||||
if (assembly != nullptr)
|
||||
{
|
||||
auto& classes = const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses());
|
||||
auto& classes = assembly->GetTypeClasses();
|
||||
if (classes.ContainsKey(klass->GetFullName()))
|
||||
{
|
||||
LOG(Warning, "Class '{0}' was already added to assembly '{1}'", String(klass->GetFullName()), String(assembly->GetName()));
|
||||
|
||||
@@ -1091,6 +1091,11 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
|
||||
return _classes;
|
||||
}
|
||||
|
||||
MAssembly::ClassesDictionary& MAssembly::GetTypeClasses() const
|
||||
{
|
||||
return const_cast<ClassesDictionary&>(GetClasses());
|
||||
}
|
||||
|
||||
bool MAssembly::Load(MonoImage* monoImage)
|
||||
{
|
||||
if (IsLoaded())
|
||||
|
||||
@@ -303,6 +303,11 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
|
||||
return _classes;
|
||||
}
|
||||
|
||||
MAssembly::ClassesDictionary& MAssembly::GetTypeClasses() const
|
||||
{
|
||||
return const_cast<ClassesDictionary&>(GetClasses());
|
||||
}
|
||||
|
||||
bool MAssembly::LoadCorlib()
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
@@ -26,5 +27,6 @@ public class AudioTool : EngineModule
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
files.Add(Path.Combine(FolderPath, "AudioTool.h"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,31 +415,53 @@ bool TextureTool::UpdateTexture(GPUContext* context, GPUTexture* texture, int32
|
||||
if (textureFormat != dataFormat)
|
||||
{
|
||||
PROFILE_CPU_NAMED("ConvertTexture");
|
||||
auto dataSampler = PixelFormatSampler::Get(dataFormat);
|
||||
auto textureSampler = PixelFormatSampler::Get(textureFormat);
|
||||
if (!dataSampler || !textureSampler)
|
||||
return true;
|
||||
|
||||
int32 mipWidth, mipHeight, mipDepth;
|
||||
texture->GetMipSize(mipIndex, mipWidth, mipHeight, mipDepth);
|
||||
|
||||
auto tempRowPitch = mipWidth * textureSampler->PixelSize;
|
||||
auto tempSlicePitch = tempRowPitch * mipHeight;
|
||||
tempData.Resize(tempSlicePitch * mipDepth);
|
||||
|
||||
ASSERT(data.Length() / rowPitch >= (uint32)mipHeight);
|
||||
for (int32 y = 0; y < mipHeight; y++)
|
||||
auto dataSampler = PixelFormatSampler::Get(dataFormat);
|
||||
auto textureSampler = PixelFormatSampler::Get(textureFormat);
|
||||
if (dataSampler && textureSampler)
|
||||
{
|
||||
for (int32 x = 0; x < mipWidth; x++)
|
||||
// Conversion with an in-built samplers
|
||||
auto tempRowPitch = mipWidth * textureSampler->PixelSize;
|
||||
auto tempSlicePitch = tempRowPitch * mipHeight;
|
||||
tempData.Resize(tempSlicePitch * mipDepth);
|
||||
ASSERT(data.Length() / rowPitch >= (uint32)mipHeight);
|
||||
for (int32 y = 0; y < mipHeight; y++)
|
||||
{
|
||||
Color color = dataSampler->SamplePoint(data.Get(), x, y, rowPitch);
|
||||
textureSampler->Store(tempData.Get(), x, y, tempRowPitch, color);
|
||||
for (int32 x = 0; x < mipWidth; x++)
|
||||
{
|
||||
Color color = dataSampler->SamplePoint(data.Get(), x, y, rowPitch);
|
||||
textureSampler->Store(tempData.Get(), x, y, tempRowPitch, color);
|
||||
}
|
||||
}
|
||||
data = ToSpan(tempData);
|
||||
rowPitch = tempRowPitch;
|
||||
slicePitch = tempSlicePitch;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Conversion with external library
|
||||
TextureData src, dst;
|
||||
src.Width = mipWidth;
|
||||
src.Height = mipHeight;
|
||||
src.Depth = mipDepth;
|
||||
src.Format = dataFormat;
|
||||
auto& srcItem = src.Items.AddOne();
|
||||
auto& srcMip = srcItem.Mips.AddOne();
|
||||
srcMip.RowPitch = rowPitch;
|
||||
srcMip.DepthPitch = slicePitch;
|
||||
srcMip.Lines = slicePitch / rowPitch;
|
||||
srcMip.Data.Link(data);
|
||||
if (Convert(dst, src, textureFormat))
|
||||
return true;
|
||||
auto& dstMip = dst.Items[0].Mips[0];
|
||||
tempData.Set(dstMip.Data.Get(), dstMip.Data.Length());
|
||||
data = ToSpan(tempData);
|
||||
rowPitch = dstMip.RowPitch;
|
||||
slicePitch = dstMip.DepthPitch;
|
||||
}
|
||||
|
||||
data = ToSpan(tempData);
|
||||
rowPitch = tempRowPitch;
|
||||
slicePitch = tempSlicePitch;
|
||||
}
|
||||
|
||||
// Update texture
|
||||
|
||||
@@ -379,6 +379,9 @@ namespace FlaxEngine.GUI
|
||||
var leftEdge = selection.StartIndex <= textBlock.Range.StartIndex ? textBlock.Bounds.UpperLeft : GetCharPosition(selection.StartIndex, out _);
|
||||
var rightEdge = selection.EndIndex >= textBlock.Range.EndIndex ? textBlock.Bounds.UpperRight : GetCharPosition(selection.EndIndex, out _);
|
||||
float height = font.Height;
|
||||
#if PLATFORM_MAC && !PLATFORM_SDL
|
||||
height /= (float)Platform.Dpi / 96.0f; // TODO: refactor DPI support on macOS to skip such hacks
|
||||
#endif
|
||||
float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f);
|
||||
alpha *= alpha;
|
||||
Color selectionColor = Color.White * alpha;
|
||||
|
||||
@@ -253,8 +253,11 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
var leftEdge = font.GetCharPosition(text, SelectionLeft, ref _layout);
|
||||
var rightEdge = font.GetCharPosition(text, SelectionRight, ref _layout);
|
||||
var fontHeight = font.Height;
|
||||
var textHeight = fontHeight / DpiScale;
|
||||
float fontHeight = font.Height;
|
||||
#if PLATFORM_MAC && !PLATFORM_SDL
|
||||
fontHeight /= (float)Platform.Dpi / 96.0f; // TODO: refactor DPI support on macOS to skip such hacks
|
||||
#endif
|
||||
float textHeight = fontHeight / DpiScale;
|
||||
|
||||
// Draw selection background
|
||||
float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f);
|
||||
|
||||
@@ -556,6 +556,9 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
const float caretWidth = 1.2f;
|
||||
Float2 caretPos = GetCharPosition(CaretPosition, out var height);
|
||||
#if PLATFORM_MAC && !PLATFORM_SDL
|
||||
height /= (float)Platform.Dpi / 96.0f; // TODO: refactor DPI support on macOS to skip such hacks
|
||||
#endif
|
||||
return new Rectangle(
|
||||
caretPos.X - (caretWidth * 0.5f),
|
||||
caretPos.Y,
|
||||
|
||||
@@ -287,7 +287,11 @@ public:
|
||||
{
|
||||
int32 hintIndex = (int32)(intptr)box->Connections[k];
|
||||
TmpConnectionHint hint = tmpHints[hintIndex];
|
||||
box->Connections[k] = hint.Node->GetBox(hint.BoxID);
|
||||
Box* hintBox = hint.Node->TryGetBox(hint.BoxID);
|
||||
if (hintBox == nullptr)
|
||||
box->Connections.RemoveAtKeepOrder(k--);
|
||||
else
|
||||
box->Connections[k] = hintBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK
|
||||
@@ -19,4 +20,12 @@ public class FidelityFX : HeaderOnlyModule
|
||||
// Merge third-party modules into engine binary
|
||||
BinaryModuleName = "FlaxEngine";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
base.GetFilesToDeploy(files);
|
||||
|
||||
files.AddRange(Directory.GetFiles(FolderPath, "*.h", SearchOption.AllDirectories));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,11 +97,22 @@ namespace Flax.Build.Bindings
|
||||
UniqueFunctionNames = new HashSet<string>();
|
||||
int idx = 1;
|
||||
functionInfo.UniqueName = functionInfo.Name;
|
||||
while (UniqueFunctionNames.Contains(functionInfo.UniqueName))
|
||||
while (!IsUniqueFunctionName(this, functionInfo.UniqueName))
|
||||
functionInfo.UniqueName = functionInfo.Name + idx++;
|
||||
UniqueFunctionNames.Add(functionInfo.UniqueName);
|
||||
}
|
||||
|
||||
private static bool IsUniqueFunctionName(VirtualClassInfo type, string name)
|
||||
{
|
||||
while (type != null)
|
||||
{
|
||||
while (type.UniqueFunctionNames.Contains(name))
|
||||
return false;
|
||||
type = type.BaseType as VirtualClassInfo;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract int GetScriptVTableSize(out int offset);
|
||||
|
||||
public abstract int GetScriptVTableOffset(VirtualClassInfo classInfo);
|
||||
|
||||
@@ -284,9 +284,17 @@ namespace Flax.Build
|
||||
if (buildData.TargetOptions.NugetPackageReferences.Any())
|
||||
{
|
||||
var nugetPath = Utilities.GetNugetPackagesPath();
|
||||
var restoreOnce = true;
|
||||
foreach (var reference in buildOptions.NugetPackageReferences)
|
||||
{
|
||||
var path = reference.GetLibPath(nugetPath);
|
||||
if (!File.Exists(path) && restoreOnce)
|
||||
{
|
||||
// Package binaries folder is missing so restore packages (incl. dependency packages)
|
||||
RestoreNugetPackages(graph, buildOptions.Target, buildOptions);
|
||||
restoreOnce = false;
|
||||
path = reference.GetLibPath(nugetPath);
|
||||
}
|
||||
args.Add(string.Format("/reference:\"{0}\"", path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,9 @@ namespace Flax.Build.NativeCpp
|
||||
{
|
||||
if (libFolder == null)
|
||||
libFolder = GetLibFolder(nugetPath);
|
||||
var dlls = Directory.GetFiles(libFolder, "*.dll", SearchOption.TopDirectoryOnly);
|
||||
if (libFolder == string.Empty)
|
||||
return string.Empty;
|
||||
var dlls = Directory.Exists(libFolder) ? Directory.GetFiles(libFolder, "*.dll", SearchOption.TopDirectoryOnly) : [];
|
||||
if (dlls.Length == 0)
|
||||
{
|
||||
Log.Error($"Missing NuGet package \"{Name}, {Version}, {Framework}\" binaries (folder: {libFolder})");
|
||||
|
||||
@@ -185,6 +185,15 @@ namespace Flax.Deploy
|
||||
plist = plist.Replace("{Arch}", arch == TargetArchitecture.ARM64 ? "arm64" : "x86_64");
|
||||
File.WriteAllText(Path.Combine(appContentsPath, "Info.plist"), plist, Encoding.ASCII);
|
||||
|
||||
// Codesign tint compiler executable and remove ones for Windows/Linux
|
||||
var webPlatformFolder = Path.Combine(OutputPath, "Source/Platforms/Web");
|
||||
if (Directory.Exists(webPlatformFolder))
|
||||
{
|
||||
Utilities.DirectoryDelete(Path.Combine(webPlatformFolder, "Binaries/Tools/Linux"));
|
||||
Utilities.DirectoryDelete(Path.Combine(webPlatformFolder, "Binaries/Tools/Windows"));
|
||||
CodeSign(Path.Combine(webPlatformFolder, "Binaries/Tools/Mac/ARM64/tint"));
|
||||
}
|
||||
|
||||
// Copy output editor files
|
||||
Utilities.DirectoryCopy(OutputPath, appContentsPath);
|
||||
|
||||
@@ -348,7 +357,8 @@ namespace Flax.Deploy
|
||||
DeployFile(src, dst, "MoltenVK_icd.json");
|
||||
DeployFiles(src, dst, "*.dll");
|
||||
DeployFiles(src, dst, "*.dylib");
|
||||
DeployFile(src, dst, "Logo.png");
|
||||
if (EngineConfiguration.UseSDL && MacConfiguration.UseSDL)
|
||||
DeployFile(src, dst, "Logo.png");
|
||||
|
||||
// Optimize package size
|
||||
Utilities.Run("strip", "FlaxEditor", null, dst, Utilities.RunOptions.None);
|
||||
|
||||
Reference in New Issue
Block a user