Merge remote-tracking branch 'origin/1.12' into 1.13

This commit is contained in:
2026-04-22 17:56:23 +02:00
98 changed files with 1087 additions and 489 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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 />
+1 -2
View File
@@ -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();
+37 -12
View File
@@ -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)
{
+210
View File
@@ -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;
}
+2 -4
View File
@@ -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"));
}
}
-5
View File
@@ -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>
+1 -1
View File
@@ -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);
-1
View File
@@ -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"
+6
View File
@@ -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;
+2 -2
View File
@@ -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"}";
}
}
}
+17 -8
View File
@@ -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;
+10 -1
View File
@@ -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)
+4 -6
View File
@@ -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
-2
View File
@@ -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;
}
+2 -1
View File
@@ -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);
+1 -99
View File
@@ -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;
+3 -101
View File
@@ -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)
+1 -1
View File
@@ -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();
+2
View File
@@ -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;
}
+7 -6
View File
@@ -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)
+3 -2
View File
@@ -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
+4 -10
View File
@@ -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++)
+83 -15
View File
@@ -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()
+5
View File
@@ -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;
+130
View File
@@ -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);
}
}
+6 -1
View File
@@ -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)
+12 -1
View File
@@ -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();
+1
View File
@@ -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();
+1
View File
@@ -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
+4
View File
@@ -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);
+17
View File
@@ -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);
+7
View File
@@ -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);
+2 -1
View File
@@ -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
+10 -3
View File
@@ -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()));
+5
View File
@@ -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())
+5
View File
@@ -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"));
}
}
+39 -17
View File
@@ -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;
+5 -2
View File
@@ -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,
+5 -1
View File
@@ -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
View File
@@ -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);