Merge branch 'master' into ImprovementSlider

This commit is contained in:
Phantom
2026-05-28 13:54:03 +02:00
23 changed files with 275 additions and 236 deletions
@@ -12,7 +12,7 @@ namespace FlaxEngine.Tools
{
partial struct Options
{
private bool ShowBtiDepth => Format != AudioFormat.Vorbis;
private bool ShowBitDepth => Format != AudioFormat.Vorbis;
}
}
}
@@ -0,0 +1,109 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEditor.Content.Thumbnails;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Content
{
/// <summary>
/// A base class for <see cref="MaterialBase"/> asset proxy object.
/// </summary>
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
public abstract class MaterialBaseProxy : BinaryAssetProxy
{
/// <summary>
/// The material preview drawer.
/// </summary>
protected MaterialPreview _preview;
/// <inheritdoc />
public override bool CanCreate(ContentFolder targetLocation)
{
return targetLocation.CanHaveAssets;
}
/// <inheritdoc />
public override void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
{
base.OnContentWindowContextMenu(menu, item);
if (item is BinaryAssetItem binaryAssetItem)
{
var button = menu.AddButton("Create Material Instance", CreateMaterialInstanceClicked);
button.Tag = binaryAssetItem;
}
}
private void CreateMaterialInstanceClicked(ContextMenuButton button)
{
var binaryAssetItem = (BinaryAssetItem)button.Tag;
CreateMaterialInstance(binaryAssetItem);
}
/// <summary>
/// Creates the material instance from the given material.
/// </summary>
/// <param name="materialItem">The material item to use as a base material.</param>
public static void CreateMaterialInstance(BinaryAssetItem materialItem)
{
var materialInstanceName = materialItem.ShortName + " Instance";
var materialInstanceProxy = Editor.Instance.ContentDatabase.GetProxy<MaterialInstance>();
Editor.Instance.Windows.ContentWin.NewItem(materialInstanceProxy, null, item => OnMaterialInstanceCreated(item, materialItem), materialInstanceName);
}
private static void OnMaterialInstanceCreated(ContentItem item, BinaryAssetItem materialItem)
{
var assetItem = (AssetItem)item;
var materialInstance = FlaxEngine.Content.LoadAsync<MaterialInstance>(assetItem.ID);
if (materialInstance == null || materialInstance.WaitForLoaded())
{
Editor.LogError("Failed to load created material instance.");
return;
}
materialInstance.BaseMaterial = FlaxEngine.Content.LoadAsync<MaterialBase>(materialItem.ID);
materialInstance.Save();
}
/// <inheritdoc />
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
{
if (_preview == null)
{
_preview = new MaterialPreview(false);
InitAssetPreview(_preview);
}
}
/// <inheritdoc />
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
{
_preview.Material = (MaterialBase)request.Asset;
_preview.Parent = guiRoot;
_preview.SyncBackbufferSize();
_preview.Task.OnDraw();
}
/// <inheritdoc />
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
{
_preview.Material = null;
_preview.Parent = null;
}
/// <inheritdoc />
public override void Dispose()
{
if (_preview != null)
{
_preview.Dispose();
_preview = null;
}
base.Dispose();
}
}
}
@@ -2,23 +2,18 @@
using System;
using FlaxEditor.Content.Thumbnails;
using FlaxEditor.Viewport.Previews;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Content
{
/// <summary>
/// A <see cref="MaterialInstance"/> asset proxy object.
/// </summary>
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
[ContentContextMenu("New/Material/Material Instance")]
public class MaterialInstanceProxy : BinaryAssetProxy
public class MaterialInstanceProxy : MaterialBaseProxy
{
private MaterialPreview _preview;
/// <inheritdoc />
public override string Name => "Material Instance";
@@ -34,12 +29,6 @@ namespace FlaxEditor.Content
/// <inheritdoc />
public override Type AssetType => typeof(MaterialInstance);
/// <inheritdoc />
public override bool CanCreate(ContentFolder targetLocation)
{
return targetLocation.CanHaveAssets;
}
/// <inheritdoc />
public override void Create(string outputPath, object arg)
{
@@ -47,49 +36,10 @@ namespace FlaxEditor.Content
throw new Exception("Failed to create new asset.");
}
/// <inheritdoc />
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
{
if (_preview == null)
{
_preview = new MaterialPreview(false);
InitAssetPreview(_preview);
}
}
/// <inheritdoc />
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((MaterialInstance)request.Asset);
}
/// <inheritdoc />
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
{
_preview.Material = (MaterialInstance)request.Asset;
_preview.Parent = guiRoot;
_preview.SyncBackbufferSize();
_preview.Task.OnDraw();
}
/// <inheritdoc />
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
{
_preview.Material = null;
_preview.Parent = null;
}
/// <inheritdoc />
public override void Dispose()
{
if (_preview != null)
{
_preview.Dispose();
_preview = null;
}
base.Dispose();
}
}
}
+1 -95
View File
@@ -2,24 +2,18 @@
using System;
using FlaxEditor.Content.Thumbnails;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Viewport.Previews;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Content
{
/// <summary>
/// A <see cref="Material"/> asset proxy object.
/// </summary>
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
[ContentContextMenu("New/Material/Material")]
public class MaterialProxy : BinaryAssetProxy
public class MaterialProxy : MaterialBaseProxy
{
private MaterialPreview _preview;
/// <inheritdoc />
public override string Name => "Material";
@@ -35,12 +29,6 @@ namespace FlaxEditor.Content
/// <inheritdoc />
public override Type AssetType => typeof(Material);
/// <inheritdoc />
public override bool CanCreate(ContentFolder targetLocation)
{
return targetLocation.CanHaveAssets;
}
/// <inheritdoc />
public override void Create(string outputPath, object arg)
{
@@ -48,92 +36,10 @@ namespace FlaxEditor.Content
throw new Exception("Failed to create new asset.");
}
/// <inheritdoc />
public override void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
{
base.OnContentWindowContextMenu(menu, item);
if (item is BinaryAssetItem binaryAssetItem)
{
var button = menu.AddButton("Create Material Instance", CreateMaterialInstanceClicked);
button.Tag = binaryAssetItem;
}
}
private void CreateMaterialInstanceClicked(ContextMenuButton obj)
{
var binaryAssetItem = (BinaryAssetItem)obj.Tag;
CreateMaterialInstance(binaryAssetItem);
}
/// <summary>
/// Creates the material instance from the given material.
/// </summary>
/// <param name="materialItem">The material item to use as a base material.</param>
public static void CreateMaterialInstance(BinaryAssetItem materialItem)
{
var materialInstanceName = materialItem.ShortName + " Instance";
var materialInstanceProxy = Editor.Instance.ContentDatabase.GetProxy<MaterialInstance>();
Editor.Instance.Windows.ContentWin.NewItem(materialInstanceProxy, null, item => OnMaterialInstanceCreated(item, materialItem), materialInstanceName);
}
private static void OnMaterialInstanceCreated(ContentItem item, BinaryAssetItem materialItem)
{
var assetItem = (AssetItem)item;
var materialInstance = FlaxEngine.Content.LoadAsync<MaterialInstance>(assetItem.ID);
if (materialInstance == null || materialInstance.WaitForLoaded())
{
Editor.LogError("Failed to load created material instance.");
return;
}
materialInstance.BaseMaterial = FlaxEngine.Content.LoadAsync<Material>(materialItem.ID);
materialInstance.Save();
}
/// <inheritdoc />
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
{
if (_preview == null)
{
_preview = new MaterialPreview(false);
InitAssetPreview(_preview);
}
}
/// <inheritdoc />
public override bool CanDrawThumbnail(ThumbnailRequest request)
{
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Material)request.Asset);
}
/// <inheritdoc />
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
{
_preview.Material = (Material)request.Asset;
_preview.Parent = guiRoot;
_preview.SyncBackbufferSize();
_preview.Task.OnDraw();
}
/// <inheritdoc />
public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot)
{
_preview.Material = null;
_preview.Parent = null;
}
/// <inheritdoc />
public override void Dispose()
{
if (_preview != null)
{
_preview.Dispose();
_preview = null;
}
base.Dispose();
}
}
}
@@ -589,6 +589,8 @@ namespace FlaxEditor.GUI
// Get the next item
bool controlDown = Root.GetKey(KeyboardKeys.Control);
var items = GetVisibleItems(!controlDown);
if (items.Count == 0)
return true;
var focusedIndex = items.IndexOf(focusedItem);
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
+22 -6
View File
@@ -229,6 +229,7 @@ namespace FlaxEditor.GUI.Timeline
private List<Track> _mediaMoveStartTracks;
private byte[][] _mediaMoveStartData;
private float _zoom = 1.0f;
private float _tracksVScrollTarget;
private bool _isMovingPositionHandle;
private bool _canPlayPause = true, _canStop = true;
private List<IUndoAction> _batchedUndoActions;
@@ -1301,10 +1302,13 @@ namespace FlaxEditor.GUI.Timeline
if (track.ParentTrack != null)
OnTracksOrderChanged();
track.OnSpawned();
_tracksPanelArea.ScrollViewTo(track);
MarkAsEdited();
if (withUndo)
Undo?.AddAction(new AddRemoveTrackAction(this, track, true));
// Scroll to track
_tracksPanelArea.ScrollViewTo(track);
_tracksVScrollTarget = _tracksPanelArea.VScrollBar.TargetValue;
}
/// <summary>
@@ -2033,12 +2037,24 @@ namespace FlaxEditor.GUI.Timeline
base.Update(deltaTime);
// Synchronize scroll vertical bars for tracks and media panels to keep the view in sync
var scroll1 = _tracksPanelArea.VScrollBar;
var scroll2 = _backgroundArea.VScrollBar;
if (scroll1.IsThumbClicked || _tracksPanelArea.IsMouseOver)
scroll2.TargetValue = scroll1.Value;
var tracksVScroll = _tracksPanelArea.VScrollBar;
var backgroundVScroll = _backgroundArea.VScrollBar;
bool forceBackgroundToTracksScroll = _tracksVScrollTarget > 0;
if (forceBackgroundToTracksScroll)
{
backgroundVScroll.TargetValue = tracksVScroll.Value;
if (Mathf.Abs(tracksVScroll.Value - _tracksVScrollTarget) < 0.5f)
_tracksVScrollTarget = 0f;
}
else if (tracksVScroll.IsThumbClicked || _tracksPanelArea.IsMouseOver)
{
backgroundVScroll.TargetValue = tracksVScroll.Value;
}
else
scroll1.TargetValue = scroll2.Value;
{
tracksVScroll.TargetValue = backgroundVScroll.Value;
}
// Batch undo actions
if (_batchedUndoActions != null && _batchedUndoActions.Count != 0)
@@ -2,6 +2,7 @@
using System;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Input;
using FlaxEngine;
using Object = FlaxEngine.Object;
@@ -14,7 +15,7 @@ namespace FlaxEditor.Viewport.Previews
public class AnimatedModelPreview : AssetPreview
{
private AnimatedModel _previewModel;
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton, _nodeNameSizeButton;
private bool _showNodes, _showBounds, _showFloor, _showNodesNames;
private StaticModel _floorModel;
private bool _playAnimation, _playAnimationOnce, _autoAdjustCamera = true;
@@ -110,9 +111,16 @@ namespace FlaxEditor.Viewport.Previews
ShowDebugDraw = true;
if (_showNodesNamesButton != null)
_showNodesNamesButton.Checked = value;
if (_nodeNameSizeButton != null)
_nodeNameSizeButton.Enabled = value;
}
}
/// <summary>
/// The font size used in the node name debug draw.
/// </summary>
public int NodeNamesSize = 10;
/// <summary>
/// Gets or sets a value indicating whether show animated model bounding box debug view.
/// </summary>
@@ -210,6 +218,15 @@ namespace FlaxEditor.Viewport.Previews
_showFloorButton.CloseMenuOnClick = false;
}
_nodeNameSizeButton = ViewWidgetButtonMenu.AddButton("Skeleton Names Size");
_nodeNameSizeButton.CloseMenuOnClick = false;
var nodeNameSizeValue = new IntValueBox(NodeNamesSize, 118, 2, 70.0f, 1, 32)
{
Parent = _nodeNameSizeButton
};
_nodeNameSizeButton.Enabled = ShowNodesNames;
nodeNameSizeValue.ValueChanged += () => NodeNamesSize = nodeNameSizeValue.Value;
// Enable shadows
PreviewLight.ShadowsMode = ShadowsCastingMode.All;
PreviewLight.CascadeCount = 3;
@@ -378,7 +395,7 @@ namespace FlaxEditor.Viewport.Previews
if (nodesMask != null && !nodesMask[nodeIndex])
continue;
//var t = new Transform(pose[nodeIndex].TranslationVector, Quaternion.Identity, new Float3(0.1f));
DebugDraw.DrawText(nodes[nodeIndex].Name, pose[nodeIndex].TranslationVector, Color.White, 20, 0.0f, 0.1f);
DebugDraw.DrawText(nodes[nodeIndex].Name, pose[nodeIndex].TranslationVector, Color.White, NodeNamesSize, 0.0f, 0.25f);
}
}
}
@@ -1,6 +1,5 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Input;
using FlaxEngine;
using FlaxEditor.Viewport.Widgets;
@@ -14,6 +13,7 @@ namespace FlaxEditor.Viewport.Previews
/// <seealso cref="AnimatedModelPreview" />
public class AnimationPreview : AnimatedModelPreview
{
private bool _baseModelMissing;
private ViewportWidgetButton _playPauseButton;
/// <summary>
@@ -94,14 +94,23 @@ namespace FlaxEditor.Viewport.Previews
var style = Style.Current;
var skinnedModel = SkinnedModel;
var baseModelMissing = false;
if (skinnedModel == null)
{
Render2D.DrawText(style.FontLarge, "Missing Base Model", new Rectangle(Float2.Zero, Size), Color.Red, TextAlignment.Center, TextAlignment.Center, TextWrapping.WrapWords);
baseModelMissing = true;
}
else if (!skinnedModel.IsLoaded)
{
Render2D.DrawText(style.FontLarge, skinnedModel.LastLoadFailed ? "Failed to load" : "Loading...", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
baseModelMissing = true;
}
if (_baseModelMissing && !baseModelMissing)
{
// Focus model when base model appears
ResetCamera();
}
_baseModelMissing = baseModelMissing;
}
/// <inheritdoc />
+1 -1
View File
@@ -123,7 +123,7 @@ namespace FlaxEditor.Windows
private readonly ScaledRenderOutputControl _viewport;
private readonly GameRoot _guiRoot;
private bool _showGUI = true, _editGUI = true;
private bool _showDebugDraw = false;
private bool _showDebugDraw = true;
private bool _audioMuted = false;
private float _audioVolume = 1;
private bool _isMaximized = false, _isUnlockingMouse = false;
@@ -61,14 +61,14 @@ void AnimGraphBase::Clear()
StateTransitions.Resize(0);
// Base
GraphType::Clear();
VisjectGraph::Clear();
}
#if USE_EDITOR
void AnimGraphBase::GetReferences(Array<Guid>& output) const
{
GraphType::GetReferences(output);
VisjectGraph::GetReferences(output);
// Collect references from nested graph (assets used in state machines)
for (const auto* subGraph : SubGraphs)
@@ -163,7 +163,7 @@ VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box)
// Add to the calling stack
VisualScripting::StackFrame frame = *stack.Stack;
frame.Node = parentNode;
frame.Node = (VisualScriptGraphNode*)parentNode;
frame.Box = box;
frame.PreviousFrame = stack.Stack;
stack.Stack = &frame;
@@ -189,7 +189,7 @@ VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box)
VisjectExecutor::Graph* VisualScriptExecutor::GetCurrentGraph() const
{
auto& stack = ThreadStacks.Get();
return stack.Stack && stack.Stack->Script ? &stack.Stack->Script->Graph : nullptr;
return stack.Stack && stack.Stack->Script ? (Graph*)&stack.Stack->Script->Graph : nullptr;
}
void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value)
@@ -432,7 +432,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
// Call Impulse or Pure Method
if (boxBase->ID == 0 || (bool)node->Values[3])
{
auto& cache = node->Data.InvokeMethod;
auto& cache = ((VisualScriptGraphNode*)node)->Data.InvokeMethod;
if (!cache.Method)
{
// Load method signature
@@ -667,7 +667,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
// Get Field
case 7:
{
auto& cache = node->Data.GetSetField;
auto& cache = ((VisualScriptGraphNode*)node)->Data.GetSetField;
if (!cache.Field)
{
const auto typeName = (StringView)node->Values[0];
@@ -753,7 +753,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
// Get Field
case 8:
{
auto& cache = node->Data.GetSetField;
auto& cache = ((VisualScriptGraphNode*)node)->Data.GetSetField;
if (!cache.Field)
{
const auto typeName = (StringView)node->Values[0];
+38 -2
View File
@@ -12,11 +12,47 @@
#define VISUAL_SCRIPT_GRAPH_MAX_CALL_STACK 250
#define VISUAL_SCRIPT_DEBUGGING USE_EDITOR
#define VisualScriptGraphNode VisjectGraphNode<>
class VisualScripting;
class VisualScriptingBinaryModule;
/// <summary>
/// Visual Script graph node.
/// </summary>
class VisualScriptGraphNode : public VisjectGraphNode<>
{
public:
struct InvokeMethodData
{
void* Method;
BinaryModule* Module;
int32 ParamsCount;
uint32 OutParamsMask;
bool IsStatic;
};
struct GetSetFieldData
{
void* Field;
BinaryModule* Module;
bool IsStatic;
};
/// <summary>
/// Custom cached data per node type. Compact to use as small amount of memory as possible.
/// </summary>
struct AdditionalData
{
union
{
InvokeMethodData InvokeMethod;
GetSetFieldData GetSetField;
};
};
// The custom per-node data. Used to cache data for faster usage at runtime.
AdditionalData Data;
};
/// <summary>
/// The Visual Script graph data.
/// </summary>
@@ -76,9 +76,10 @@ void GPUTasksContext::OnFrameBegin()
{
auto task = _tasksSyncing[i];
auto state = task->GetState();
if (EnumHasAllFlags(task->Flags, ObjectFlags::WasMarkedToDelete))
state = TaskState::Finished;
if (task->GetSyncPoint() <= _currentSyncPoint && state != TaskState::Finished)
{
// TODO: add stats counter and count performed jobs, print to log on exit.
task->Sync();
}
if (state == TaskState::Failed || state == TaskState::Canceled)
+10 -5
View File
@@ -621,7 +621,7 @@ namespace FlaxEngine
{
ibData = dataPtr[IB];
use16BitIndexBuffer = _formats[IB] == PixelFormat.R16_UInt;
triangles = (uint)(_data[IB].Length / PixelFormatExtensions.SizeInBytes(_formats[IB]));
triangles = (uint)(_data[IB].Length / (PixelFormatExtensions.SizeInBytes(_formats[IB]) * 3));
}
if (mesh.Init(vertices, triangles, vbData, ibData, use16BitIndexBuffer, vbLayout))
@@ -643,11 +643,16 @@ namespace FlaxEngine
else
{
Float3 min = Float3.Maximum, max = Float3.Minimum;
for (int i = 0; i < vertices; i++)
PixelFormatSampler.Get(positionStream.Format, out var positionSampler);
int positionStride = positionStream.Stride;
fixed (byte* data = positionStream.Data)
{
Float3 pos = positionStream.GetFloat3(i);
Float3.Min(ref min, ref pos, out min);
Float3.Max(ref max, ref pos, out max);
for (int i = 0; i < vertices; i++)
{
Float3 pos = new Float3(positionSampler.Read(data + i * positionStride));
Float3.Min(ref min, ref pos, out min);
Float3.Max(ref max, ref pos, out max);
}
}
bounds = new BoundingBox(min, max);
}
+27 -17
View File
@@ -98,99 +98,99 @@ public:
/// <summary>
/// The skinned model asset used for rendering.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Skeleton\")")
AssetReference<SkinnedModel> SkinnedModel;
/// <summary>
/// The animation graph asset used for the skinned mesh skeleton bones evaluation (controls the animation).
/// </summary>
API_FIELD(Attributes="EditorOrder(15), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(15), DefaultValue(null), EditorDisplay(\"Skeleton\")")
AssetReference<AnimationGraph> AnimationGraph;
/// <summary>
/// If true, use per-bone motion blur on this skeletal model. It requires additional rendering, can be disabled to save performance.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Drawing\")")
bool PerBoneMotionBlur = true;
/// <summary>
/// If true, animation speed will be affected by the global timescale parameter.
/// </summary>
API_FIELD(Attributes="EditorOrder(30), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(30), DefaultValue(true), EditorDisplay(\"Updating\")")
bool UseTimeScale = true;
/// <summary>
/// If true, the animation will be updated even when an actor cannot be seen by any camera. Otherwise, the animations themselves will also stop running when the actor is off-screen.
/// </summary>
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Updating\")")
bool UpdateWhenOffscreen = false;
/// <summary>
/// The animation update delta timescale. Can be used to speed up animation playback or create slow motion effect.
/// </summary>
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Updating\")")
float UpdateSpeed = 1.0f;
/// <summary>
/// The animation update mode. Can be used to optimize the performance.
/// </summary>
API_FIELD(Attributes="EditorOrder(50), DefaultValue(AnimationUpdateMode.Auto), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(50), DefaultValue(AnimationUpdateMode.Auto), EditorDisplay(\"Updating\")")
AnimationUpdateMode UpdateMode = AnimationUpdateMode::Auto;
/// <summary>
/// The master scale parameter for the actor bounding box. Helps to reduce mesh flickering effect on screen edges.
/// </summary>
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Drawing\")")
float BoundsScale = 1.5f;
/// <summary>
/// The custom bounds(in actor local space). If set to empty bounds then source skinned model bind pose bounds will be used.
/// </summary>
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Drawing\")")
BoundingBox CustomBounds = BoundingBox::Zero;
/// <summary>
/// The model Level Of Detail bias value. Allows to increase or decrease rendered model quality.
/// </summary>
API_FIELD(Attributes="EditorOrder(80), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Skinned Model\", \"LOD Bias\")")
API_FIELD(Attributes="EditorOrder(80), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Drawing\", \"LOD Bias\")")
int32 LODBias = 0;
/// <summary>
/// Gets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature.
/// </summary>
API_FIELD(Attributes="EditorOrder(90), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Skinned Model\", \"Forced LOD\")")
API_FIELD(Attributes="EditorOrder(90), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Drawing\", \"Forced LOD\")")
int32 ForcedLOD = -1;
/// <summary>
/// The draw passes to use for rendering this object.
/// </summary>
API_FIELD(Attributes="EditorOrder(100), DefaultValue(DrawPass.Default), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(100), DefaultValue(DrawPass.Default), EditorDisplay(\"Drawing\")")
DrawPass DrawModes = DrawPass::Default;
/// <summary>
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Skinned Model\"), EditorOrder(110), DefaultValue(0)")
API_FIELD(Attributes="EditorOrder(110), DefaultValue(0), EditorDisplay(\"Drawing\")")
int8 SortOrder = 0;
/// <summary>
/// The shadows casting mode.
/// [Deprecated on 26.10.2022, expires on 26.10.2024]
/// </summary>
API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Drawing\")")
DEPRECATED() ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
/// <summary>
/// The animation root motion apply target. If not specified the animated model will apply it itself.
/// </summary>
API_FIELD(Attributes="EditorOrder(120), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(120), DefaultValue(null), EditorDisplay(\"Skeleton\")")
ScriptingObjectReference<Actor> RootMotionTarget;
#if USE_EDITOR
/// <summary>
/// If checked, the skeleton pose will be shawn during debug shapes drawing.
/// If checked, the skeleton pose will be shown during debug shapes drawing.
/// </summary>
API_FIELD(Attributes="EditorOrder(200), EditorDisplay(\"Skinned Model\")") bool ShowDebugDrawSkeleton = false;
API_FIELD(Attributes="EditorOrder(200), EditorDisplay(\"Skeleton\"), VisibleIf(nameof(ShowDebugDrawOptions))") bool ShowDebugDrawSkeleton = false;
#endif
public:
@@ -440,6 +440,16 @@ public:
API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
private:
#if USE_EDITOR
/// <summary>
/// Used to hide <see cref="ShowDebugDrawSkeleton"/> options if when the skinned model or animation graph is null.
/// </summary>
API_PROPERTY(Attributes="HideInEditor, NoSerialize") bool GetShowDebugDrawOptions() const
{
return SkinnedModel != nullptr && AnimationGraph != nullptr;
}
#endif
void ApplyRootMotion(const Transform& rootMotionDelta);
void SyncParameters();
void RunBlendShapeDeformer(const MeshBase* mesh, struct MeshDeformationData& deformation);
+1 -1
View File
@@ -70,7 +70,7 @@ public:
/// <summary>
/// The size of a single sample in bits. The clip will be converted to this bit depth on import.
/// </summary>
API_FIELD(Attributes="EditorOrder(50), VisibleIf(nameof(ShowBtiDepth))")
API_FIELD(Attributes="EditorOrder(50), VisibleIf(nameof(ShowBitDepth))")
BitDepth BitDepth = BitDepth::_16;
String ToString() const;
+9
View File
@@ -167,6 +167,15 @@ public:
// Base
return Base::onNodeLoaded(n);
}
void Clear() override
{
FloatCurves.Clear();
Float2Curves.Clear();
Float3Curves.Clear();
Float4Curves.Clear();
Base::Clear();
}
};
/// <summary>
+1 -1
View File
@@ -952,7 +952,7 @@ void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
#define SAMPLE_CURVE(id, curves, type, graphType) \
case id: \
{ \
const auto& curve = GetCurrentGraph()->curves[node->Data.Curve.CurveIndex]; \
const auto& curve = GetCurrentGraph()->curves[node->CurveIndex]; \
const float time = (float)tryGetValue(node->GetBox(0), Value::Zero); \
value.Type = VariantType(VariantType::graphType); \
curve.Evaluate(*(type*)value.AsData, time, false); \
+11 -42
View File
@@ -42,42 +42,6 @@ public:
template<class BoxType = VisjectGraphBox>
class VisjectGraphNode : public GraphNode<BoxType>
{
public:
struct CurveData
{
/// <summary>
/// The curve index.
/// </summary>
int32 CurveIndex;
};
/// <summary>
/// Custom cached data per node type. Compact to use as small amount of memory as possible.
/// </summary>
struct AdditionalData
{
union
{
CurveData Curve;
struct
{
void* Method;
BinaryModule* Module;
int32 ParamsCount;
uint32 OutParamsMask;
bool IsStatic;
} InvokeMethod;
struct
{
void* Field;
BinaryModule* Module;
bool IsStatic;
} GetSetField;
};
};
public:
VisjectGraphNode()
: GraphNode<BoxType>()
@@ -85,10 +49,7 @@ public:
}
public:
/// <summary>
/// The custom data (depends on node type). Used to cache data for faster usage at runtime.
/// </summary>
AdditionalData Data;
int32 CurveIndex = MAX_uint16;
/// <summary>
/// The asset references. Linked resources such as Animation assets are referenced in graph data as ID. We need to keep valid refs to them at runtime to keep data in memory.
@@ -148,7 +109,7 @@ public:
#define SETUP_CURVE(id, curves, access) \
case id: \
{ \
n->Data.Curve.CurveIndex = curves.Count(); \
n->CurveIndex = curves.Count(); \
auto& curve = curves.AddOne(); \
const int32 keyframesCount = n->Values[0].AsInt; \
auto& keyframes = curve.GetKeyframes(); \
@@ -177,9 +138,17 @@ public:
}
}
// Base
return Base::onNodeLoaded(n);
}
void Clear() override
{
FloatCurves.Clear();
Float2Curves.Clear();
Float3Curves.Clear();
Float4Curves.Clear();
Base::Clear();
}
};
/// <summary>
@@ -367,7 +367,7 @@ namespace Flax.Build.Platforms
if (args.All(arg => !arg.Contains("-sASSERTIONS")))
{
// minimum_runtime_check.js from Emscripten checks min browser versions only with ASSERTIONS enabled so use custom check
var checkBrowserVersion = File.ReadAllText(Path.Combine(Globals.EngineRoot, "Source/Platforms/Web/Binaries/Data/check_browser_version.js"));
var checkBrowserVersion = File.ReadAllText(Path.Combine(Globals.EngineRoot, "Source/Platforms/Web/Binaries/check_browser_version.js"));
checkBrowserVersion = checkBrowserVersion.Replace("TARGET_NOT_SUPPORTED", "0x7fffffff");
checkBrowserVersion = checkBrowserVersion.Replace("MIN_CHROME_VERSION", minChrome.ToString());
checkBrowserVersion = checkBrowserVersion.Replace("MIN_FIREFOX_VERSION", minFirefox.ToString());
@@ -377,11 +377,11 @@ namespace Flax.Build.Platforms
args.Add($"--pre-js \"{path}\"");
}
if (addJSPI)
args.Add($"--pre-js \"{Globals.EngineRoot}/Source/Platforms/Web/Binaries/Data/check_jspi.js\"");
args.Add($"--pre-js \"{Globals.EngineRoot}/Source/Platforms/Web/Binaries/check_jspi.js\"");
// Customize output HTML shell
if (options.LinkEnv.Output == LinkerOutput.Executable)
args.Add($"--shell-file \"{Globals.EngineRoot}/Source/Platforms/Web/Binaries/Data/shell.html\"");
args.Add($"--shell-file \"{Globals.EngineRoot}/Source/Platforms/Web/Binaries/shell.html\"");
}
args.Add("-Wl,--start-group");