Merge remote-tracking branch 'origin/master' into 1.9
# Conflicts: # Content/Editor/Camera/M_Camera.flax # Content/Editor/CubeTexturePreviewMaterial.flax # Content/Editor/DebugMaterials/DDGIDebugProbes.flax # Content/Editor/DebugMaterials/SingleColor/Decal.flax # Content/Editor/DebugMaterials/SingleColor/Particle.flax # Content/Editor/DebugMaterials/SingleColor/Surface.flax # Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax # Content/Editor/DebugMaterials/SingleColor/Terrain.flax # Content/Editor/DefaultFontMaterial.flax # Content/Editor/Gizmo/FoliageBrushMaterial.flax # Content/Editor/Gizmo/Material.flax # Content/Editor/Gizmo/MaterialWire.flax # Content/Editor/Gizmo/SelectionOutlineMaterial.flax # Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax # Content/Editor/Highlight Material.flax # Content/Editor/Icons/IconsMaterial.flax # Content/Editor/IesProfilePreviewMaterial.flax # Content/Editor/Particles/Particle Material Color.flax # Content/Editor/Particles/Smoke Material.flax # Content/Editor/SpriteMaterial.flax # Content/Editor/Terrain/Circle Brush Material.flax # Content/Editor/Terrain/Highlight Terrain Material.flax # Content/Editor/TexturePreviewMaterial.flax # Content/Editor/Wires Debug Material.flax # Content/Engine/DefaultDeformableMaterial.flax # Content/Engine/DefaultMaterial.flax # Content/Engine/DefaultTerrainMaterial.flax # Content/Engine/SingleColorMaterial.flax # Content/Engine/SkyboxMaterial.flax # Source/Engine/Graphics/Materials/MaterialShader.h
This commit is contained in:
@@ -430,7 +430,9 @@ float3x4 GetPrevBoneMatrix(int index)
|
|||||||
float3 SkinPrevPosition(ModelInput_Skinned input)
|
float3 SkinPrevPosition(ModelInput_Skinned input)
|
||||||
{
|
{
|
||||||
float4 position = float4(input.Position.xyz, 1);
|
float4 position = float4(input.Position.xyz, 1);
|
||||||
float3x4 boneMatrix = input.BlendWeights.x * GetPrevBoneMatrix(input.BlendIndices.x);
|
float weightsSum = input.BlendWeights.x + input.BlendWeights.y + input.BlendWeights.z + input.BlendWeights.w;
|
||||||
|
float mainWeight = input.BlendWeights.x + (1.0f - weightsSum); // Re-normalize to account for 16-bit weights encoding erros
|
||||||
|
float3x4 boneMatrix = mainWeight * GetPrevBoneMatrix(input.BlendIndices.x);
|
||||||
boneMatrix += input.BlendWeights.y * GetPrevBoneMatrix(input.BlendIndices.y);
|
boneMatrix += input.BlendWeights.y * GetPrevBoneMatrix(input.BlendIndices.y);
|
||||||
boneMatrix += input.BlendWeights.z * GetPrevBoneMatrix(input.BlendIndices.z);
|
boneMatrix += input.BlendWeights.z * GetPrevBoneMatrix(input.BlendIndices.z);
|
||||||
boneMatrix += input.BlendWeights.w * GetPrevBoneMatrix(input.BlendIndices.w);
|
boneMatrix += input.BlendWeights.w * GetPrevBoneMatrix(input.BlendIndices.w);
|
||||||
@@ -439,12 +441,6 @@ float3 SkinPrevPosition(ModelInput_Skinned input)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Cached skinning data to avoid multiple calculation
|
|
||||||
struct SkinningData
|
|
||||||
{
|
|
||||||
float3x4 BlendMatrix;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculates the transposed transform matrix for the given bone index
|
// Calculates the transposed transform matrix for the given bone index
|
||||||
float3x4 GetBoneMatrix(int index)
|
float3x4 GetBoneMatrix(int index)
|
||||||
{
|
{
|
||||||
@@ -457,7 +453,9 @@ float3x4 GetBoneMatrix(int index)
|
|||||||
// Calculates the transposed transform matrix for the given vertex (uses blending)
|
// Calculates the transposed transform matrix for the given vertex (uses blending)
|
||||||
float3x4 GetBoneMatrix(ModelInput_Skinned input)
|
float3x4 GetBoneMatrix(ModelInput_Skinned input)
|
||||||
{
|
{
|
||||||
float3x4 boneMatrix = input.BlendWeights.x * GetBoneMatrix(input.BlendIndices.x);
|
float weightsSum = input.BlendWeights.x + input.BlendWeights.y + input.BlendWeights.z + input.BlendWeights.w;
|
||||||
|
float mainWeight = input.BlendWeights.x + (1.0f - weightsSum); // Re-normalize to account for 16-bit weights encoding erros
|
||||||
|
float3x4 boneMatrix = mainWeight * GetBoneMatrix(input.BlendIndices.x);
|
||||||
boneMatrix += input.BlendWeights.y * GetBoneMatrix(input.BlendIndices.y);
|
boneMatrix += input.BlendWeights.y * GetBoneMatrix(input.BlendIndices.y);
|
||||||
boneMatrix += input.BlendWeights.z * GetBoneMatrix(input.BlendIndices.z);
|
boneMatrix += input.BlendWeights.z * GetBoneMatrix(input.BlendIndices.z);
|
||||||
boneMatrix += input.BlendWeights.w * GetBoneMatrix(input.BlendIndices.w);
|
boneMatrix += input.BlendWeights.w * GetBoneMatrix(input.BlendIndices.w);
|
||||||
@@ -465,13 +463,13 @@ float3x4 GetBoneMatrix(ModelInput_Skinned input)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transforms the vertex position by weighted sum of the skinning matrices
|
// Transforms the vertex position by weighted sum of the skinning matrices
|
||||||
float3 SkinPosition(ModelInput_Skinned input, SkinningData data)
|
float3 SkinPosition(ModelInput_Skinned input, float3x4 boneMatrix)
|
||||||
{
|
{
|
||||||
return mul(data.BlendMatrix, float4(input.Position.xyz, 1));
|
return mul(boneMatrix, float4(input.Position.xyz, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transforms the vertex position by weighted sum of the skinning matrices
|
// Transforms the vertex position by weighted sum of the skinning matrices
|
||||||
float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data)
|
float3x3 SkinTangents(ModelInput_Skinned input, float3x4 boneMatrix)
|
||||||
{
|
{
|
||||||
// Unpack vertex tangent frame
|
// Unpack vertex tangent frame
|
||||||
float bitangentSign = input.Tangent.w ? -1.0f : +1.0f;
|
float bitangentSign = input.Tangent.w ? -1.0f : +1.0f;
|
||||||
@@ -479,10 +477,10 @@ float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data)
|
|||||||
float3 tangent = input.Tangent.xyz * 2.0 - 1.0;
|
float3 tangent = input.Tangent.xyz * 2.0 - 1.0;
|
||||||
|
|
||||||
// Apply skinning
|
// Apply skinning
|
||||||
tangent = mul(data.BlendMatrix, float4(tangent, 0));
|
tangent = normalize(mul(boneMatrix, float4(tangent, 0)));
|
||||||
normal = mul(data.BlendMatrix, float4(normal, 0));
|
normal = normalize(mul(boneMatrix, float4(normal, 0)));
|
||||||
|
|
||||||
float3 bitangent = cross(normal, tangent) * bitangentSign;
|
float3 bitangent = normalize(cross(normal, tangent) * bitangentSign);
|
||||||
return float3x3(tangent, bitangent, normal);
|
return float3x3(tangent, bitangent, normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,10 +499,9 @@ VertexOutput VS_Skinned(ModelInput_Skinned input)
|
|||||||
VertexOutput output;
|
VertexOutput output;
|
||||||
|
|
||||||
// Perform skinning
|
// Perform skinning
|
||||||
SkinningData data;
|
float3x4 boneMatrix = GetBoneMatrix(input);
|
||||||
data.BlendMatrix = GetBoneMatrix(input);
|
float3 position = SkinPosition(input, boneMatrix);
|
||||||
float3 position = SkinPosition(input, data);
|
float3x3 tangentToLocal = SkinTangents(input, boneMatrix);
|
||||||
float3x3 tangentToLocal = SkinTangents(input, data);
|
|
||||||
|
|
||||||
// Compute world space vertex position
|
// Compute world space vertex position
|
||||||
CalculateInstanceTransform(input);
|
CalculateInstanceTransform(input);
|
||||||
|
|||||||
@@ -236,6 +236,7 @@
|
|||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:String x:Key="/Default/GrammarAndSpelling/GrammarChecking/RulesStates/=LanguageTool_002EEN_002EE_005FG/@EntryIndexedValue">DisabledByUser</s:String>
|
||||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/@KeyIndexDefined">True</s:Boolean>
|
<s:Boolean x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/@KeyIndexDefined">True</s:Boolean>
|
||||||
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/Color/@EntryValue">Blue</s:String>
|
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/Color/@EntryValue">Blue</s:String>
|
||||||
<s:Boolean x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/MatchComments/@EntryValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/MatchComments/@EntryValue">True</s:Boolean>
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ namespace FlaxEditor.Content
|
|||||||
private ScriptMemberInfo[] _parameters;
|
private ScriptMemberInfo[] _parameters;
|
||||||
private ScriptMemberInfo[] _methods;
|
private ScriptMemberInfo[] _methods;
|
||||||
private object[] _attributes;
|
private object[] _attributes;
|
||||||
|
private List<Action<ScriptType>> _disposing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Visual Script asset that contains this type.
|
/// Gets the Visual Script asset that contains this type.
|
||||||
@@ -310,6 +311,13 @@ namespace FlaxEditor.Content
|
|||||||
|
|
||||||
internal void Dispose()
|
internal void Dispose()
|
||||||
{
|
{
|
||||||
|
if (_disposing != null)
|
||||||
|
{
|
||||||
|
foreach (var e in _disposing)
|
||||||
|
e(new ScriptType(this));
|
||||||
|
_disposing.Clear();
|
||||||
|
_disposing = null;
|
||||||
|
}
|
||||||
if (_parameters != null)
|
if (_parameters != null)
|
||||||
{
|
{
|
||||||
OnAssetReloading(_asset);
|
OnAssetReloading(_asset);
|
||||||
@@ -510,6 +518,14 @@ namespace FlaxEditor.Content
|
|||||||
}
|
}
|
||||||
return _methods;
|
return _methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void TrackLifetime(Action<ScriptType> disposing)
|
||||||
|
{
|
||||||
|
if (_disposing == null)
|
||||||
|
_disposing = new List<Action<ScriptType>>();
|
||||||
|
_disposing.Add(disposing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -209,16 +209,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||||
{
|
{
|
||||||
var result = base.OnDragMove(ref location, data);
|
var result = base.OnDragMove(ref location, data);
|
||||||
if (result != DragDropEffect.None)
|
if (result != DragDropEffect.None || _dragHandlers == null)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
return _dragHandlers.Effect;
|
return _dragHandlers.Effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDragLeave()
|
public override void OnDragLeave()
|
||||||
{
|
{
|
||||||
_dragHandlers.OnDragLeave();
|
_dragHandlers?.OnDragLeave();
|
||||||
|
|
||||||
base.OnDragLeave();
|
base.OnDragLeave();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -585,7 +585,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||||
{
|
{
|
||||||
var result = base.OnDragMove(ref location, data);
|
var result = base.OnDragMove(ref location, data);
|
||||||
if (result != DragDropEffect.None)
|
if (result != DragDropEffect.None || _dragHandlers == null)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
return _dragHandlers.Effect;
|
return _dragHandlers.Effect;
|
||||||
@@ -594,7 +594,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDragLeave()
|
public override void OnDragLeave()
|
||||||
{
|
{
|
||||||
_dragHandlers.OnDragLeave();
|
_dragHandlers?.OnDragLeave();
|
||||||
|
|
||||||
base.OnDragLeave();
|
base.OnDragLeave();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -554,7 +554,7 @@ namespace FlaxEditor.GUI
|
|||||||
// Check if has selected item
|
// Check if has selected item
|
||||||
if (_selectedIndices != null && _selectedIndices.Count > 0)
|
if (_selectedIndices != null && _selectedIndices.Count > 0)
|
||||||
{
|
{
|
||||||
string text = _selectedIndices.Count == 1 ? _items[_selectedIndices[0]] : "Multiple Values";
|
string text = _selectedIndices.Count == 1 ? (_selectedIndices[0] >= 0 && _selectedIndices[0] < _items.Count ? _items[_selectedIndices[0]] : "") : "Multiple Values";
|
||||||
|
|
||||||
// Draw text of the selected item
|
// Draw text of the selected item
|
||||||
float textScale = Height / DefaultHeight;
|
float textScale = Height / DefaultHeight;
|
||||||
|
|||||||
@@ -42,15 +42,14 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
|
|
||||||
// Arrange controls
|
// Arrange controls
|
||||||
Margin margin = _menu._itemsMargin;
|
Margin margin = _menu._itemsMargin;
|
||||||
float y = margin.Top;
|
float y = 0;
|
||||||
float x = margin.Left;
|
|
||||||
float width = Width - margin.Width;
|
float width = Width - margin.Width;
|
||||||
for (int i = 0; i < _children.Count; i++)
|
for (int i = 0; i < _children.Count; i++)
|
||||||
{
|
{
|
||||||
if (_children[i] is ContextMenuItem item && item.Visible)
|
if (_children[i] is ContextMenuItem item && item.Visible)
|
||||||
{
|
{
|
||||||
var height = item.Height;
|
var height = item.Height;
|
||||||
item.Bounds = new Rectangle(x, y, width, height);
|
item.Bounds = new Rectangle(margin.Left, y, width, height);
|
||||||
y += height + margin.Height;
|
y += height + margin.Height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,7 +299,6 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
if (_panel.Children[i] is ContextMenuChildMenu menu && menu.Text == text)
|
if (_panel.Children[i] is ContextMenuChildMenu menu && menu.Text == text)
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +317,6 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
Parent = _panel
|
Parent = _panel
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,10 +393,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
float height = _itemsAreaMargin.Height;
|
float height = _itemsAreaMargin.Height;
|
||||||
int itemsLeft = MaximumItemsInViewCount;
|
int itemsLeft = MaximumItemsInViewCount;
|
||||||
int overflowItemCount = 0;
|
int overflowItemCount = 0;
|
||||||
|
int itemsCount = 0;
|
||||||
for (int i = 0; i < _panel.Children.Count; i++)
|
for (int i = 0; i < _panel.Children.Count; i++)
|
||||||
{
|
{
|
||||||
if (_panel.Children[i] is ContextMenuItem item && item.Visible)
|
if (_panel.Children[i] is ContextMenuItem item && item.Visible)
|
||||||
{
|
{
|
||||||
|
itemsCount++;
|
||||||
if (itemsLeft > 0)
|
if (itemsLeft > 0)
|
||||||
{
|
{
|
||||||
height += item.Height + _itemsMargin.Height;
|
height += item.Height + _itemsMargin.Height;
|
||||||
@@ -412,6 +411,8 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
maxWidth = Mathf.Max(maxWidth, item.MinimumWidth);
|
maxWidth = Mathf.Max(maxWidth, item.MinimumWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (itemsCount != 0)
|
||||||
|
height -= _itemsMargin.Height; // Remove item margin from top and bottom
|
||||||
maxWidth = Mathf.Max(maxWidth + 20, MinimumWidth);
|
maxWidth = Mathf.Max(maxWidth + 20, MinimumWidth);
|
||||||
|
|
||||||
// Move child arrows to accommodate scroll bar showing
|
// Move child arrows to accommodate scroll bar showing
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
CloseMenuOnClick = false;
|
CloseMenuOnClick = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ShowChild(ContextMenu parentContextMenu)
|
||||||
|
{
|
||||||
|
// Hide parent CM popups and set itself as child
|
||||||
|
var vAlign = parentContextMenu.ItemsAreaMargin.Top;
|
||||||
|
var location = new Float2(Width, -vAlign);
|
||||||
|
location = PointToParent(parentContextMenu, location);
|
||||||
|
parentContextMenu.ShowChild(ContextMenu, location);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
@@ -58,14 +67,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
var parentContextMenu = ParentContextMenu;
|
var parentContextMenu = ParentContextMenu;
|
||||||
if (parentContextMenu == ContextMenu)
|
if (parentContextMenu == ContextMenu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ContextMenu.IsOpened)
|
if (ContextMenu.IsOpened)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
base.OnMouseEnter(location);
|
base.OnMouseEnter(location);
|
||||||
|
|
||||||
// Hide parent CM popups and set itself as child
|
ShowChild(parentContextMenu);
|
||||||
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -78,8 +85,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
if (ContextMenu.IsOpened)
|
if (ContextMenu.IsOpened)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Hide parent CM popups and set itself as child
|
ShowChild(parentContextMenu);
|
||||||
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
|
|
||||||
return base.OnMouseUp(location, button);
|
return base.OnMouseUp(location, button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
if (Type == ScriptType.Null)
|
if (Type == ScriptType.Null)
|
||||||
{
|
{
|
||||||
Editor.LogError("Missing anim event type " + _instanceTypeName);
|
Editor.LogError("Missing anim event type " + _instanceTypeName);
|
||||||
|
InitMissing();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Instance = (AnimEvent)Type.CreateInstance();
|
Instance = (AnimEvent)Type.CreateInstance();
|
||||||
@@ -125,20 +126,37 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
_isRegisteredForScriptsReload = true;
|
_isRegisteredForScriptsReload = true;
|
||||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||||
}
|
}
|
||||||
|
Type.TrackLifetime(OnTypeDisposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTypeDisposing(ScriptType type)
|
||||||
|
{
|
||||||
|
if (Type == type && !IsDisposing)
|
||||||
|
{
|
||||||
|
// Turn into missing script
|
||||||
|
OnScriptsReloadBegin();
|
||||||
|
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
|
||||||
|
InitMissing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitMissing()
|
||||||
|
{
|
||||||
|
CanDelete = true;
|
||||||
|
CanSplit = false;
|
||||||
|
CanResize = false;
|
||||||
|
TooltipText = $"Missing Anim Event Type '{_instanceTypeName}'";
|
||||||
|
BackgroundColor = Color.Red;
|
||||||
|
Type = ScriptType.Null;
|
||||||
|
Instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitMissing(string typeName, byte[] data)
|
internal void InitMissing(string typeName, byte[] data)
|
||||||
{
|
{
|
||||||
Type = ScriptType.Null;
|
|
||||||
IsContinuous = false;
|
IsContinuous = false;
|
||||||
CanDelete = true;
|
|
||||||
CanSplit = false;
|
|
||||||
CanResize = false;
|
|
||||||
TooltipText = $"Missing Anim Event Type '{typeName}'";
|
|
||||||
Instance = null;
|
|
||||||
BackgroundColor = Color.Red;
|
|
||||||
_instanceTypeName = typeName;
|
_instanceTypeName = typeName;
|
||||||
_instanceData = data;
|
_instanceData = data;
|
||||||
|
InitMissing();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Load(BinaryReader stream)
|
internal void Load(BinaryReader stream)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "Engine/Engine/CommandLine.h"
|
#include "Engine/Engine/CommandLine.h"
|
||||||
#include "Engine/Renderer/ProbesRenderer.h"
|
#include "Engine/Renderer/ProbesRenderer.h"
|
||||||
#include "Engine/Animations/Graph/AnimGraph.h"
|
#include "Engine/Animations/Graph/AnimGraph.h"
|
||||||
|
#include "Engine/Core/ObjectsRemovalService.h"
|
||||||
|
|
||||||
ManagedEditor::InternalOptions ManagedEditor::ManagedEditorOptions;
|
ManagedEditor::InternalOptions ManagedEditor::ManagedEditorOptions;
|
||||||
|
|
||||||
@@ -572,6 +573,29 @@ bool ManagedEditor::EvaluateVisualScriptLocal(VisualScript* script, VisualScript
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ManagedEditor::WipeOutLeftoverSceneObjects()
|
||||||
|
{
|
||||||
|
Array<ScriptingObject*> objects = Scripting::GetObjects();
|
||||||
|
bool removedAny = false;
|
||||||
|
for (ScriptingObject* object : objects)
|
||||||
|
{
|
||||||
|
if (EnumHasAllFlags(object->Flags, ObjectFlags::IsDuringPlay) && EnumHasNoneFlags(object->Flags, ObjectFlags::WasMarkedToDelete))
|
||||||
|
{
|
||||||
|
if (auto* sceneObject = Cast<SceneObject>(object))
|
||||||
|
{
|
||||||
|
if (sceneObject->HasParent())
|
||||||
|
continue; // Skip sub-objects
|
||||||
|
|
||||||
|
LOG(Error, "Object '{}' (ID={}, Type={}) is still in memory after play end but should be destroyed (memory leak).", sceneObject->GetNamePath(), sceneObject->GetID(), sceneObject->GetType().ToString());
|
||||||
|
sceneObject->DeleteObject();
|
||||||
|
removedAny = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removedAny)
|
||||||
|
ObjectsRemovalService::Flush();
|
||||||
|
}
|
||||||
|
|
||||||
void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly)
|
void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly)
|
||||||
{
|
{
|
||||||
ASSERT(!HasManagedInstance());
|
ASSERT(!HasManagedInstance());
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ public:
|
|||||||
API_FUNCTION(Internal) static VisualScriptStackFrame GetVisualScriptPreviousScopeFrame();
|
API_FUNCTION(Internal) static VisualScriptStackFrame GetVisualScriptPreviousScopeFrame();
|
||||||
API_FUNCTION(Internal) static Array<VisualScriptLocal> GetVisualScriptLocals();
|
API_FUNCTION(Internal) static Array<VisualScriptLocal> GetVisualScriptLocals();
|
||||||
API_FUNCTION(Internal) static bool EvaluateVisualScriptLocal(VisualScript* script, API_PARAM(Ref) VisualScriptLocal& local);
|
API_FUNCTION(Internal) static bool EvaluateVisualScriptLocal(VisualScript* script, API_PARAM(Ref) VisualScriptLocal& local);
|
||||||
|
API_FUNCTION(Internal) static void WipeOutLeftoverSceneObjects();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
||||||
|
|||||||
@@ -144,5 +144,11 @@ namespace FlaxEditor.Scripting
|
|||||||
{
|
{
|
||||||
return Utils.GetEmptyArray<ScriptMemberInfo>();
|
return Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void TrackLifetime(Action<ScriptType> disposing)
|
||||||
|
{
|
||||||
|
ElementType.TrackLifetime(disposing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,6 +167,12 @@ namespace FlaxEditor.Scripting
|
|||||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||||
/// <returns>An array of member objects representing all methods defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no methods are defined for the current type, or if none of the defined methods match the binding constraints.</returns>
|
/// <returns>An array of member objects representing all methods defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no methods are defined for the current type, or if none of the defined methods match the binding constraints.</returns>
|
||||||
ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr);
|
ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers delegate to be invoked upon script type disposal (except hot-reload in Editor via <see cref="ScriptsBuilder.ScriptsReload"/>). For example, can happen when user deleted Visual Script asset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">Event to call when script type gets disposed (eg. removed asset).</param>
|
||||||
|
void TrackLifetime(Action<ScriptType> disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1395,11 +1395,21 @@ namespace FlaxEditor.Scripting
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Basic check to see if a type could be casted to another type
|
/// Registers delegate to be invoked upon script type disposal (except hot-reload in Editor via <see cref="ScriptsBuilder.ScriptsReload"/>). For example, can happen when user deleted Visual Script asset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">Event to call when script type gets disposed (eg. removed asset).</param>
|
||||||
|
public void TrackLifetime(Action<ScriptType> disposing)
|
||||||
|
{
|
||||||
|
if (_custom != null)
|
||||||
|
_custom.TrackLifetime(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Basic check to see if a type could be cast to another type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="from">Source type</param>
|
/// <param name="from">Source type</param>
|
||||||
/// <param name="to">Target type</param>
|
/// <param name="to">Target type</param>
|
||||||
/// <returns>True if the type can be casted</returns>
|
/// <returns>True if the type can be cast.</returns>
|
||||||
public static bool CanCast(ScriptType from, ScriptType to)
|
public static bool CanCast(ScriptType from, ScriptType to)
|
||||||
{
|
{
|
||||||
if (from == to)
|
if (from == to)
|
||||||
@@ -1412,10 +1422,10 @@ namespace FlaxEditor.Scripting
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Basic check to see if this type could be casted to another type
|
/// Basic check to see if this type could be cast to another type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="to">Target type</param>
|
/// <param name="to">Target type</param>
|
||||||
/// <returns>True if the type can be casted</returns>
|
/// <returns>True if the type can be cast.</returns>
|
||||||
public bool CanCastTo(ScriptType to)
|
public bool CanCastTo(ScriptType to)
|
||||||
{
|
{
|
||||||
return CanCast(this, to);
|
return CanCast(this, to);
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ namespace FlaxEditor.States
|
|||||||
Editor.OnPlayBegin();
|
Editor.OnPlayBegin();
|
||||||
IsPlayModeStarting = false;
|
IsPlayModeStarting = false;
|
||||||
Profiler.EndEvent();
|
Profiler.EndEvent();
|
||||||
|
|
||||||
|
Time.Synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupEditorEnvOptions()
|
private void SetupEditorEnvOptions()
|
||||||
@@ -192,7 +194,7 @@ namespace FlaxEditor.States
|
|||||||
|
|
||||||
// Restore editor scene
|
// Restore editor scene
|
||||||
SceneRestoring?.Invoke();
|
SceneRestoring?.Invoke();
|
||||||
_duplicateScenes.DeletedScenes();
|
_duplicateScenes.UnloadScenes();
|
||||||
PluginManager.Internal_DeinitializeGamePlugins();
|
PluginManager.Internal_DeinitializeGamePlugins();
|
||||||
Editor.Internal_SetPlayMode(false);
|
Editor.Internal_SetPlayMode(false);
|
||||||
_duplicateScenes.RestoreSceneData();
|
_duplicateScenes.RestoreSceneData();
|
||||||
@@ -209,6 +211,8 @@ namespace FlaxEditor.States
|
|||||||
Editor.OnPlayEnd();
|
Editor.OnPlayEnd();
|
||||||
IsPlayModeEnding = false;
|
IsPlayModeEnding = false;
|
||||||
Profiler.EndEvent();
|
Profiler.EndEvent();
|
||||||
|
|
||||||
|
Time.Synchronize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.IO;
|
|||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
using FlaxEditor.GUI.Input;
|
using FlaxEditor.GUI.Input;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Surface.Undo;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -16,14 +17,13 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
public abstract class BlendPointsEditor : ContainerControl
|
public class BlendPointsEditor : ContainerControl
|
||||||
{
|
{
|
||||||
|
private readonly Animation.MultiBlend _node;
|
||||||
private readonly bool _is2D;
|
private readonly bool _is2D;
|
||||||
private Float2 _rangeX;
|
private Float2 _rangeX, _rangeY;
|
||||||
private Float2 _rangeY;
|
private Float2 _debugPos = Float2.Minimum;
|
||||||
private readonly BlendPoint[] _blendPoints = new BlendPoint[Animation.MultiBlend.MaxAnimationsCount];
|
private readonly List<BlendPoint> _blendPoints = new List<BlendPoint>();
|
||||||
private readonly Guid[] _pointsAnims = new Guid[Animation.MultiBlend.MaxAnimationsCount];
|
|
||||||
private readonly Float2[] _pointsLocations = new Float2[Animation.MultiBlend.MaxAnimationsCount];
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents single blend point.
|
/// Represents single blend point.
|
||||||
@@ -31,16 +31,22 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||||
protected class BlendPoint : Control
|
protected class BlendPoint : Control
|
||||||
{
|
{
|
||||||
private static Matrix3x3 _transform = Matrix3x3.RotationZ(45.0f * Mathf.DegreesToRadians) * Matrix3x3.Translation2D(4.0f, 0.5f);
|
|
||||||
private readonly BlendPointsEditor _editor;
|
private readonly BlendPointsEditor _editor;
|
||||||
private readonly int _index;
|
private readonly int _index;
|
||||||
private bool _isMouseDown;
|
private Float2 _mousePosOffset;
|
||||||
|
private bool _isMouseDown, _mouseMoved;
|
||||||
|
private object[] _mouseMoveStartValues;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default size for the blend points.
|
/// The default size for the blend points.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float DefaultSize = 8.0f;
|
public const float DefaultSize = 8.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blend point index.
|
||||||
|
/// </summary>
|
||||||
|
public int Index => _index;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -53,24 +59,45 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_index = index;
|
_index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EndMove()
|
||||||
|
{
|
||||||
|
_isMouseDown = false;
|
||||||
|
EndMouseCapture();
|
||||||
|
if (_mouseMoveStartValues != null)
|
||||||
|
{
|
||||||
|
// Add undo action
|
||||||
|
_editor._node.Surface.AddBatchedUndoAction(new EditNodeValuesAction(_editor._node, _mouseMoveStartValues, true));
|
||||||
|
_mouseMoveStartValues = null;
|
||||||
|
}
|
||||||
|
if (_mouseMoved)
|
||||||
|
_editor._node.Surface.MarkAsEdited();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnGotFocus()
|
public override void OnGotFocus()
|
||||||
{
|
{
|
||||||
base.OnGotFocus();
|
base.OnGotFocus();
|
||||||
|
|
||||||
_editor.SelectedIndex = _index;
|
_editor._node.SelectedAnimationIndex = _index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
// Cache data
|
// Draw dot with outline
|
||||||
var isSelected = _editor.SelectedIndex == _index;
|
var style = Style.Current;
|
||||||
|
var icon = Editor.Instance.Icons.VisjectBoxClosed32;
|
||||||
// Draw rotated rectangle
|
var size = Height;
|
||||||
Render2D.PushTransform(ref _transform);
|
var rect = new Rectangle(new Float2(size * -0.5f) + Size * 0.5f, new Float2(size));
|
||||||
Render2D.FillRectangle(new Rectangle(0, 0, 5, 5), isSelected ? Color.Orange : Color.BlueViolet);
|
var outline = Color.Black; // Shadow
|
||||||
Render2D.PopTransform();
|
if (_isMouseDown)
|
||||||
|
outline = style.SelectionBorder;
|
||||||
|
else if (IsMouseOver)
|
||||||
|
outline = style.BorderHighlighted;
|
||||||
|
else if (_editor._node.SelectedAnimationIndex == _index)
|
||||||
|
outline = style.BackgroundSelected;
|
||||||
|
Render2D.DrawSprite(icon, rect.MakeExpanded(4.0f), outline);
|
||||||
|
Render2D.DrawSprite(icon, rect, style.Foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -80,6 +107,9 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
Focus();
|
Focus();
|
||||||
_isMouseDown = true;
|
_isMouseDown = true;
|
||||||
|
_mouseMoved = false;
|
||||||
|
_mouseMoveStartValues = null;
|
||||||
|
_mousePosOffset = -location;
|
||||||
StartMouseCapture();
|
StartMouseCapture();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -92,8 +122,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
if (button == MouseButton.Left && _isMouseDown)
|
if (button == MouseButton.Left && _isMouseDown)
|
||||||
{
|
{
|
||||||
_isMouseDown = false;
|
EndMove();
|
||||||
EndMouseCapture();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,9 +132,26 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnMouseMove(Float2 location)
|
public override void OnMouseMove(Float2 location)
|
||||||
{
|
{
|
||||||
if (_isMouseDown)
|
if (_isMouseDown && (_mouseMoved || Float2.DistanceSquared(location, _mousePosOffset) > 16.0f))
|
||||||
{
|
{
|
||||||
_editor.SetLocation(_index, _editor.BlendPointPosToBlendSpacePos(Location + location));
|
if (!_mouseMoved)
|
||||||
|
{
|
||||||
|
// Capture initial state for undo
|
||||||
|
_mouseMoved = true;
|
||||||
|
_mouseMoveStartValues = _editor._node.Surface.Undo != null ? (object[])_editor._node.Values.Clone() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newLocation = Location + location + _mousePosOffset;
|
||||||
|
newLocation = _editor.BlendPointPosToBlendSpacePos(newLocation);
|
||||||
|
if (Root != null && Root.GetKey(KeyboardKeys.Control))
|
||||||
|
{
|
||||||
|
var data0 = (Float4)_editor._node.Values[0];
|
||||||
|
var rangeX = new Float2(data0.X, data0.Y);
|
||||||
|
var rangeY = _editor._is2D ? new Float2(data0.Z, data0.W) : Float2.One;
|
||||||
|
var grid = new Float2(Mathf.Abs(rangeX.Y - rangeX.X) * 0.01f, Mathf.Abs(rangeY.X - rangeY.Y) * 0.01f);
|
||||||
|
newLocation = Float2.SnapToGrid(newLocation, grid);
|
||||||
|
}
|
||||||
|
_editor.SetLocation(_index, newLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnMouseMove(location);
|
base.OnMouseMove(location);
|
||||||
@@ -116,8 +162,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
if (_isMouseDown)
|
if (_isMouseDown)
|
||||||
{
|
{
|
||||||
_isMouseDown = false;
|
EndMove();
|
||||||
EndMouseCapture();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnMouseLeave();
|
base.OnMouseLeave();
|
||||||
@@ -128,8 +173,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
if (_isMouseDown)
|
if (_isMouseDown)
|
||||||
{
|
{
|
||||||
_isMouseDown = false;
|
EndMove();
|
||||||
EndMouseCapture();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnLostFocus();
|
base.OnLostFocus();
|
||||||
@@ -149,40 +193,130 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Is2D => _is2D;
|
public bool Is2D => _is2D;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blend points count.
|
||||||
|
/// </summary>
|
||||||
|
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="node">The node.</param>
|
||||||
/// <param name="is2D">The value indicating whether blend space is 2D, otherwise it is 1D.</param>
|
/// <param name="is2D">The value indicating whether blend space is 2D, otherwise it is 1D.</param>
|
||||||
/// <param name="x">The X location.</param>
|
/// <param name="x">The X location.</param>
|
||||||
/// <param name="y">The Y location.</param>
|
/// <param name="y">The Y location.</param>
|
||||||
/// <param name="width">The width.</param>
|
/// <param name="width">The width.</param>
|
||||||
/// <param name="height">The height.</param>
|
/// <param name="height">The height.</param>
|
||||||
public BlendPointsEditor(bool is2D, float x, float y, float width, float height)
|
public BlendPointsEditor(Animation.MultiBlend node, bool is2D, float x, float y, float width, float height)
|
||||||
: base(x, y, width, height)
|
: base(x, y, width, height)
|
||||||
{
|
{
|
||||||
|
_node = node;
|
||||||
_is2D = is2D;
|
_is2D = is2D;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
internal void AddPoint()
|
||||||
/// Gets the blend space data.
|
{
|
||||||
/// </summary>
|
// Add random point within range
|
||||||
/// <param name="rangeX">The space range for X axis (X-width, Y-height).</param>
|
var rand = new Float2(Mathf.Frac((float)Platform.TimeSeconds), (Platform.TimeCycles % 10000) / 10000.0f);
|
||||||
/// <param name="rangeY">The space range for Y axis (X-width, Y-height).</param>
|
AddPoint(Float2.Lerp(new Float2(_rangeX.X, _rangeY.X), new Float2(_rangeX.Y, _rangeY.Y), rand));
|
||||||
/// <param name="pointsAnims">The points anims (input array to fill of size equal 14).</param>
|
}
|
||||||
/// <param name="pointsLocations">The points locations (input array to fill of size equal 14).</param>
|
|
||||||
public abstract void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations);
|
private void AddPoint(Float2 location)
|
||||||
|
{
|
||||||
|
// Reuse existing animation
|
||||||
|
var count = PointsCount;
|
||||||
|
Guid id = Guid.Empty;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
id = (Guid)_node.Values[5 + i * 2];
|
||||||
|
if (id != Guid.Empty)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (id == Guid.Empty)
|
||||||
|
{
|
||||||
|
// Just use the first anim from project, user will change it
|
||||||
|
var ids = FlaxEngine.Content.GetAllAssetsByType(typeof(FlaxEngine.Animation));
|
||||||
|
if (ids.Length != 0)
|
||||||
|
id = ids[0];
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddPoint(id, location);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the index of the selected blend point.
|
/// Sets the blend point asset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract int SelectedIndex { get; set; }
|
/// <param name="asset">The asset.</param>
|
||||||
|
/// <param name="location">The location.</param>
|
||||||
|
public void AddPoint(Guid asset, Float2 location)
|
||||||
|
{
|
||||||
|
// Find the first free slot
|
||||||
|
var count = PointsCount;
|
||||||
|
if (count == Animation.MultiBlend.MaxAnimationsCount)
|
||||||
|
return;
|
||||||
|
var values = (object[])_node.Values.Clone();
|
||||||
|
var index = 0;
|
||||||
|
for (; index < count; index++)
|
||||||
|
{
|
||||||
|
var dataB = (Guid)_node.Values[5 + index * 2];
|
||||||
|
if (dataB == Guid.Empty)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index == count)
|
||||||
|
{
|
||||||
|
// Add another blend point
|
||||||
|
Array.Resize(ref values, values.Length + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
values[4 + index * 2] = new Float4(location.X, _is2D ? location.Y : 0.0f, 0, 1.0f);
|
||||||
|
values[5 + index * 2] = asset;
|
||||||
|
_node.SetValues(values);
|
||||||
|
|
||||||
|
// Auto-select
|
||||||
|
_node.SelectedAnimationIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the blend point asset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The index.</param>
|
||||||
|
/// <param name="asset">The asset.</param>
|
||||||
|
/// <param name="withUndo">True to use undo action.</param>
|
||||||
|
public void SetAsset(int index, Guid asset, bool withUndo = true)
|
||||||
|
{
|
||||||
|
if (withUndo)
|
||||||
|
{
|
||||||
|
_node.SetValue(5 + index * 2, asset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_node.Values[5 + index * 2] = asset;
|
||||||
|
_node.Surface.MarkAsEdited();
|
||||||
|
}
|
||||||
|
|
||||||
|
_node.UpdateUI();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the blend point location.
|
/// Sets the blend point location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">The index.</param>
|
/// <param name="index">The index.</param>
|
||||||
/// <param name="location">The location.</param>
|
/// <param name="location">The location.</param>
|
||||||
public abstract void SetLocation(int index, Float2 location);
|
public void SetLocation(int index, Float2 location)
|
||||||
|
{
|
||||||
|
var dataA = (Float4)_node.Values[4 + index * 2];
|
||||||
|
var ranges = (Float4)_node.Values[0];
|
||||||
|
|
||||||
|
dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y);
|
||||||
|
if (_is2D)
|
||||||
|
dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W);
|
||||||
|
|
||||||
|
_node.Values[4 + index * 2] = dataA;
|
||||||
|
|
||||||
|
_node.UpdateUI();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the blend points area.
|
/// Gets the blend points area.
|
||||||
@@ -249,10 +383,23 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
public override void Update(float deltaTime)
|
public override void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
// Synchronize blend points collection
|
// Synchronize blend points collection
|
||||||
GetData(out _rangeX, out _rangeY, _pointsAnims, _pointsLocations);
|
var data0 = (Float4)_node.Values[0];
|
||||||
for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++)
|
_rangeX = new Float2(data0.X, data0.Y);
|
||||||
|
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
|
||||||
|
var count = PointsCount;
|
||||||
|
while (_blendPoints.Count > count)
|
||||||
{
|
{
|
||||||
if (_pointsAnims[i] != Guid.Empty)
|
_blendPoints[count].Dispose();
|
||||||
|
_blendPoints.RemoveAt(count);
|
||||||
|
}
|
||||||
|
while (_blendPoints.Count < count)
|
||||||
|
_blendPoints.Add(null);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var animId = (Guid)_node.Values[5 + i * 2];
|
||||||
|
var dataA = (Float4)_node.Values[4 + i * 2];
|
||||||
|
var location = new Float2(Mathf.Clamp(dataA.X, _rangeX.X, _rangeX.Y), _is2D ? Mathf.Clamp(dataA.Y, _rangeY.X, _rangeY.Y) : 0.0f);
|
||||||
|
if (animId != Guid.Empty)
|
||||||
{
|
{
|
||||||
if (_blendPoints[i] == null)
|
if (_blendPoints[i] == null)
|
||||||
{
|
{
|
||||||
@@ -264,7 +411,13 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update blend point
|
// Update blend point
|
||||||
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(_pointsLocations[i]);
|
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location);
|
||||||
|
var asset = Editor.Instance.ContentDatabase.FindAsset(animId);
|
||||||
|
var tooltip = asset?.ShortName ?? string.Empty;
|
||||||
|
tooltip += "\nX: " + location.X;
|
||||||
|
if (_is2D)
|
||||||
|
tooltip += "\nY: " + location.Y;
|
||||||
|
_blendPoints[i].TooltipText = tooltip;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -277,6 +430,26 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug current playback position
|
||||||
|
if (((AnimGraphSurface)_node.Surface).TryGetTraceEvent(_node, out var traceEvent))
|
||||||
|
{
|
||||||
|
if (_is2D)
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
// Unpack xy from 32-bits
|
||||||
|
Half2 packed = *(Half2*)&traceEvent.Value;
|
||||||
|
_debugPos = (Float2)packed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_debugPos = new Float2(traceEvent.Value, 0.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_debugPos = Float2.Minimum;
|
||||||
|
}
|
||||||
|
|
||||||
base.Update(deltaTime);
|
base.Update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,14 +466,90 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDown(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseUp(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (button == MouseButton.Right)
|
||||||
|
{
|
||||||
|
// Show context menu
|
||||||
|
var menu = new FlaxEditor.GUI.ContextMenu.ContextMenu();
|
||||||
|
var b = menu.AddButton("Add point", OnAddPoint);
|
||||||
|
b.Tag = location;
|
||||||
|
b.Enabled = PointsCount < Animation.MultiBlend.MaxAnimationsCount;
|
||||||
|
if (GetChildAt(location) is BlendPoint blendPoint)
|
||||||
|
{
|
||||||
|
b = menu.AddButton("Remove point", OnRemovePoint);
|
||||||
|
b.Tag = blendPoint.Index;
|
||||||
|
b.TooltipText = blendPoint.TooltipText;
|
||||||
|
}
|
||||||
|
menu.Show(this, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAddPoint(FlaxEditor.GUI.ContextMenu.ContextMenuButton b)
|
||||||
|
{
|
||||||
|
AddPoint(BlendPointPosToBlendSpacePos((Float2)b.Tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemovePoint(FlaxEditor.GUI.ContextMenu.ContextMenuButton b)
|
||||||
|
{
|
||||||
|
SetAsset((int)b.Tag, Guid.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||||
|
{
|
||||||
|
// Draw line
|
||||||
|
Render2D.DrawLine(start, end, gridColor);
|
||||||
|
|
||||||
|
// Draw label
|
||||||
|
var labelWidth = 50.0f;
|
||||||
|
var labelHeight = 10.0f;
|
||||||
|
var labelMargin = 2.0f;
|
||||||
|
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
var hAlign = TextAlignment.Near;
|
||||||
|
Rectangle labelRect;
|
||||||
|
if (vertical)
|
||||||
|
{
|
||||||
|
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||||
|
if (isLast)
|
||||||
|
return; // Don't overlap with the first horizontal label
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||||
|
if (isLast)
|
||||||
|
{
|
||||||
|
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||||
|
hAlign = TextAlignment.Far;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
// Cache data
|
|
||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
var rect = new Rectangle(Float2.Zero, Size);
|
var rect = new Rectangle(Float2.Zero, Size);
|
||||||
var containsFocus = ContainsFocus;
|
var containsFocus = ContainsFocus;
|
||||||
GetPointsArea(out var pointsArea);
|
GetPointsArea(out var pointsArea);
|
||||||
|
var data0 = (Float4)_node.Values[0];
|
||||||
|
var rangeX = new Float2(data0.X, data0.Y);
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||||
@@ -309,19 +558,27 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
// Grid
|
// Grid
|
||||||
int splits = 10;
|
int splits = 10;
|
||||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||||
|
var labelColor = style.ForegroundDisabled;
|
||||||
|
var labelFont = style.FontSmall;
|
||||||
//var blendArea = BlendAreaRect;
|
//var blendArea = BlendAreaRect;
|
||||||
var blendArea = pointsArea;
|
var blendArea = pointsArea;
|
||||||
for (int i = 0; i < splits; i++)
|
|
||||||
|
for (int i = 0; i <= splits; i++)
|
||||||
{
|
{
|
||||||
float x = blendArea.Left + blendArea.Width * i / splits;
|
float alpha = (float)i / splits;
|
||||||
Render2D.DrawLine(new Float2(x, 1), new Float2(x, rect.Height - 2), gridColor);
|
float x = blendArea.Left + blendArea.Width * alpha;
|
||||||
|
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||||
|
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||||
}
|
}
|
||||||
if (_is2D)
|
if (_is2D)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < splits; i++)
|
var rangeY = new Float2(data0.Z, data0.W);
|
||||||
|
for (int i = 0; i <= splits; i++)
|
||||||
{
|
{
|
||||||
float y = blendArea.Top + blendArea.Height * i / splits;
|
float alpha = (float)i / splits;
|
||||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
float y = blendArea.Top + blendArea.Height * alpha;
|
||||||
|
float value = Mathf.Lerp(rangeY.X, rangeY.Y, alpha);
|
||||||
|
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -330,11 +587,24 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
|
|
||||||
|
// Draw debug position
|
||||||
|
if (_debugPos.X > float.MinValue)
|
||||||
|
{
|
||||||
|
// Draw dot with outline
|
||||||
|
var icon = Editor.Instance.Icons.VisjectBoxOpen32;
|
||||||
|
var size = BlendPoint.DefaultSize;
|
||||||
|
var debugPos = BlendSpacePosToBlendPointPos(_debugPos);
|
||||||
|
var debugRect = new Rectangle(debugPos + new Float2(size * -0.5f) + size * 0.5f, new Float2(size));
|
||||||
|
var outline = Color.Black; // Shadow
|
||||||
|
Render2D.DrawSprite(icon, debugRect.MakeExpanded(2.0f), outline);
|
||||||
|
Render2D.DrawSprite(icon, debugRect, style.ProgressNormal);
|
||||||
|
}
|
||||||
|
|
||||||
// Frame
|
// Frame
|
||||||
Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), containsFocus ? style.ProgressNormal : style.BackgroundSelected);
|
var frameColor = containsFocus ? style.BackgroundSelected : (IsMouseOver ? style.ForegroundGrey : style.ForegroundDisabled);
|
||||||
|
Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), frameColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,6 +616,14 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
|
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
|
||||||
public abstract class MultiBlend : SurfaceNode
|
public abstract class MultiBlend : SurfaceNode
|
||||||
{
|
{
|
||||||
|
private Button _addButton;
|
||||||
|
private Button _removeButton;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The blend space editor.
|
||||||
|
/// </summary>
|
||||||
|
protected BlendPointsEditor _editor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The selected animation label.
|
/// The selected animation label.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -379,7 +657,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum animations amount to blend per node.
|
/// The maximum animations amount to blend per node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int MaxAnimationsCount = 14;
|
public const int MaxAnimationsCount = 255;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the index of the selected animation.
|
/// Gets or sets the index of the selected animation.
|
||||||
@@ -387,7 +665,12 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
public int SelectedAnimationIndex
|
public int SelectedAnimationIndex
|
||||||
{
|
{
|
||||||
get => _selectedAnimation.SelectedIndex;
|
get => _selectedAnimation.SelectedIndex;
|
||||||
set => _selectedAnimation.SelectedIndex = value;
|
set
|
||||||
|
{
|
||||||
|
OnSelectedAnimationPopupShowing(_selectedAnimation);
|
||||||
|
_selectedAnimation.SelectedIndex = value;
|
||||||
|
UpdateUI();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -402,20 +685,14 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Text = "Selected Animation:",
|
Text = "Selected Animation:",
|
||||||
Parent = this
|
Parent = this
|
||||||
};
|
};
|
||||||
|
|
||||||
_selectedAnimation = new ComboBox(_selectedAnimationLabel.X, 4 * layoutOffsetY, _selectedAnimationLabel.Width)
|
_selectedAnimation = new ComboBox(_selectedAnimationLabel.X, 4 * layoutOffsetY, _selectedAnimationLabel.Width)
|
||||||
{
|
{
|
||||||
|
TooltipText = "Select blend point to view and edit it",
|
||||||
Parent = this
|
Parent = this
|
||||||
};
|
};
|
||||||
|
|
||||||
_selectedAnimation.PopupShowing += OnSelectedAnimationPopupShowing;
|
_selectedAnimation.PopupShowing += OnSelectedAnimationPopupShowing;
|
||||||
_selectedAnimation.SelectedIndexChanged += OnSelectedAnimationChanged;
|
_selectedAnimation.SelectedIndexChanged += OnSelectedAnimationChanged;
|
||||||
|
|
||||||
var items = new List<string>(MaxAnimationsCount);
|
|
||||||
while (items.Count < MaxAnimationsCount)
|
|
||||||
items.Add(string.Empty);
|
|
||||||
_selectedAnimation.Items = items;
|
|
||||||
|
|
||||||
_animationPicker = new AssetPicker(new ScriptType(typeof(FlaxEngine.Animation)), new Float2(_selectedAnimation.Left, _selectedAnimation.Bottom + 4))
|
_animationPicker = new AssetPicker(new ScriptType(typeof(FlaxEngine.Animation)), new Float2(_selectedAnimation.Left, _selectedAnimation.Bottom + 4))
|
||||||
{
|
{
|
||||||
Parent = this
|
Parent = this
|
||||||
@@ -428,20 +705,36 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Text = "Speed:",
|
Text = "Speed:",
|
||||||
Parent = this
|
Parent = this
|
||||||
};
|
};
|
||||||
|
|
||||||
_animationSpeed = new FloatValueBox(1.0f, _animationSpeedLabel.Right + 4, _animationSpeedLabel.Y, _selectedAnimation.Right - _animationSpeedLabel.Right - 4)
|
_animationSpeed = new FloatValueBox(1.0f, _animationSpeedLabel.Right + 4, _animationSpeedLabel.Y, _selectedAnimation.Right - _animationSpeedLabel.Right - 4)
|
||||||
{
|
{
|
||||||
SlideSpeed = 0.01f,
|
SlideSpeed = 0.01f,
|
||||||
Parent = this
|
Parent = this
|
||||||
};
|
};
|
||||||
_animationSpeed.ValueChanged += OnAnimationSpeedValueChanged;
|
_animationSpeed.ValueChanged += OnAnimationSpeedValueChanged;
|
||||||
|
|
||||||
|
var buttonsSize = 12;
|
||||||
|
_addButton = new Button(_selectedAnimation.Right - buttonsSize, _selectedAnimation.Bottom + 4, buttonsSize, buttonsSize)
|
||||||
|
{
|
||||||
|
Text = "+",
|
||||||
|
TooltipText = "Add a new blend point",
|
||||||
|
Parent = this
|
||||||
|
};
|
||||||
|
_addButton.Clicked += OnAddButtonClicked;
|
||||||
|
_removeButton = new Button(_addButton.Left - buttonsSize - 4, _addButton.Y, buttonsSize, buttonsSize)
|
||||||
|
{
|
||||||
|
Text = "-",
|
||||||
|
TooltipText = "Remove selected blend point",
|
||||||
|
Parent = this
|
||||||
|
};
|
||||||
|
_removeButton.Clicked += OnRemoveButtonClicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelectedAnimationPopupShowing(ComboBox comboBox)
|
private void OnSelectedAnimationPopupShowing(ComboBox comboBox)
|
||||||
{
|
{
|
||||||
var items = comboBox.Items;
|
var items = comboBox.Items;
|
||||||
items.Clear();
|
items.Clear();
|
||||||
for (var i = 0; i < MaxAnimationsCount; i++)
|
var count = _editor.PointsCount;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var animId = (Guid)Values[5 + i * 2];
|
var animId = (Guid)Values[5 + i * 2];
|
||||||
var path = string.Empty;
|
var path = string.Empty;
|
||||||
@@ -484,6 +777,16 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnAddButtonClicked()
|
||||||
|
{
|
||||||
|
_editor.AddPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemoveButtonClicked()
|
||||||
|
{
|
||||||
|
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the editor UI.
|
/// Updates the editor UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -491,7 +794,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// <param name="isValid">if set to <c>true</c> is selection valid.</param>
|
/// <param name="isValid">if set to <c>true</c> is selection valid.</param>
|
||||||
/// <param name="data0">The packed data 0.</param>
|
/// <param name="data0">The packed data 0.</param>
|
||||||
/// <param name="data1">The packed data 1.</param>
|
/// <param name="data1">The packed data 1.</param>
|
||||||
protected virtual void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
public virtual void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
||||||
{
|
{
|
||||||
if (isValid)
|
if (isValid)
|
||||||
{
|
{
|
||||||
@@ -511,19 +814,21 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_animationPicker.Enabled = isValid;
|
_animationPicker.Enabled = isValid;
|
||||||
_animationSpeedLabel.Enabled = isValid;
|
_animationSpeedLabel.Enabled = isValid;
|
||||||
_animationSpeed.Enabled = isValid;
|
_animationSpeed.Enabled = isValid;
|
||||||
|
_addButton.Enabled = _editor.PointsCount < MaxAnimationsCount;
|
||||||
|
_removeButton.Enabled = isValid && data1 != Guid.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the editor UI.
|
/// Updates the editor UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void UpdateUI()
|
public void UpdateUI()
|
||||||
{
|
{
|
||||||
if (_isUpdatingUI)
|
if (_isUpdatingUI)
|
||||||
return;
|
return;
|
||||||
_isUpdatingUI = true;
|
_isUpdatingUI = true;
|
||||||
|
|
||||||
var selectedIndex = _selectedAnimation.SelectedIndex;
|
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||||
var isValid = selectedIndex != -1;
|
var isValid = selectedIndex >= 0 && selectedIndex < _editor.PointsCount;
|
||||||
Float4 data0;
|
Float4 data0;
|
||||||
Guid data1;
|
Guid data1;
|
||||||
if (isValid)
|
if (isValid)
|
||||||
@@ -549,6 +854,16 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
UpdateUI();
|
UpdateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnSpawned(SurfaceNodeActions action)
|
||||||
|
{
|
||||||
|
base.OnSpawned(action);
|
||||||
|
|
||||||
|
// Select the first animation to make setup easier
|
||||||
|
OnSelectedAnimationPopupShowing(_selectedAnimation);
|
||||||
|
_selectedAnimation.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnValuesChanged()
|
public override void OnValuesChanged()
|
||||||
{
|
{
|
||||||
@@ -566,67 +881,6 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
private readonly Label _animationXLabel;
|
private readonly Label _animationXLabel;
|
||||||
private readonly FloatValueBox _animationX;
|
private readonly FloatValueBox _animationX;
|
||||||
private readonly Editor _editor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Multi Blend 1D blend space editor.
|
|
||||||
/// </summary>
|
|
||||||
/// <seealso cref="FlaxEditor.Surface.Archetypes.BlendPointsEditor" />
|
|
||||||
protected class Editor : BlendPointsEditor
|
|
||||||
{
|
|
||||||
private MultiBlend1D _node;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Editor"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">The parent Visject Node node.</param>
|
|
||||||
/// <param name="x">The X location.</param>
|
|
||||||
/// <param name="y">The Y location.</param>
|
|
||||||
/// <param name="width">The width.</param>
|
|
||||||
/// <param name="height">The height.</param>
|
|
||||||
public Editor(MultiBlend1D node, float x, float y, float width, float height)
|
|
||||||
: base(false, x, y, width, height)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations)
|
|
||||||
{
|
|
||||||
var data0 = (Float4)_node.Values[0];
|
|
||||||
rangeX = new Float2(data0.X, data0.Y);
|
|
||||||
rangeY = Float2.Zero;
|
|
||||||
for (int i = 0; i < MaxAnimationsCount; i++)
|
|
||||||
{
|
|
||||||
var dataA = (Float4)_node.Values[4 + i * 2];
|
|
||||||
var dataB = (Guid)_node.Values[5 + i * 2];
|
|
||||||
|
|
||||||
pointsAnims[i] = dataB;
|
|
||||||
pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), 0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override int SelectedIndex
|
|
||||||
{
|
|
||||||
get => _node.SelectedAnimationIndex;
|
|
||||||
set => _node.SelectedAnimationIndex = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void SetLocation(int index, Float2 location)
|
|
||||||
{
|
|
||||||
var dataA = (Float4)_node.Values[4 + index * 2];
|
|
||||||
var ranges = (Float4)_node.Values[0];
|
|
||||||
|
|
||||||
dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y);
|
|
||||||
|
|
||||||
_node.Values[4 + index * 2] = dataA;
|
|
||||||
_node.Surface.MarkAsEdited();
|
|
||||||
|
|
||||||
_node.UpdateUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MultiBlend1D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
public MultiBlend1D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||||
@@ -646,11 +900,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
};
|
};
|
||||||
_animationX.ValueChanged += OnAnimationXChanged;
|
_animationX.ValueChanged += OnAnimationXChanged;
|
||||||
|
|
||||||
_editor = new Editor(this,
|
var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f;
|
||||||
FlaxEditor.Surface.Constants.NodeMarginX,
|
_editor = new BlendPointsEditor(this, false, FlaxEditor.Surface.Constants.NodeMarginX, _animationX.Bottom + 4.0f, size, 120.0f);
|
||||||
_animationX.Bottom + 4.0f,
|
|
||||||
Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f,
|
|
||||||
120.0f);
|
|
||||||
_editor.Parent = this;
|
_editor.Parent = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,7 +921,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
||||||
{
|
{
|
||||||
base.UpdateUI(selectedIndex, isValid, ref data0, ref data1);
|
base.UpdateUI(selectedIndex, isValid, ref data0, ref data1);
|
||||||
|
|
||||||
@@ -700,68 +951,6 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
private readonly FloatValueBox _animationX;
|
private readonly FloatValueBox _animationX;
|
||||||
private readonly Label _animationYLabel;
|
private readonly Label _animationYLabel;
|
||||||
private readonly FloatValueBox _animationY;
|
private readonly FloatValueBox _animationY;
|
||||||
private readonly Editor _editor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Multi Blend 2D blend space editor.
|
|
||||||
/// </summary>
|
|
||||||
/// <seealso cref="FlaxEditor.Surface.Archetypes.BlendPointsEditor" />
|
|
||||||
protected class Editor : BlendPointsEditor
|
|
||||||
{
|
|
||||||
private MultiBlend2D _node;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Editor"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">The parent Visject Node node.</param>
|
|
||||||
/// <param name="x">The X location.</param>
|
|
||||||
/// <param name="y">The Y location.</param>
|
|
||||||
/// <param name="width">The width.</param>
|
|
||||||
/// <param name="height">The height.</param>
|
|
||||||
public Editor(MultiBlend2D node, float x, float y, float width, float height)
|
|
||||||
: base(true, x, y, width, height)
|
|
||||||
{
|
|
||||||
_node = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations)
|
|
||||||
{
|
|
||||||
var data0 = (Float4)_node.Values[0];
|
|
||||||
rangeX = new Float2(data0.X, data0.Y);
|
|
||||||
rangeY = new Float2(data0.Z, data0.W);
|
|
||||||
for (int i = 0; i < MaxAnimationsCount; i++)
|
|
||||||
{
|
|
||||||
var dataA = (Float4)_node.Values[4 + i * 2];
|
|
||||||
var dataB = (Guid)_node.Values[5 + i * 2];
|
|
||||||
|
|
||||||
pointsAnims[i] = dataB;
|
|
||||||
pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), Mathf.Clamp(dataA.Y, rangeY.X, rangeY.Y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override int SelectedIndex
|
|
||||||
{
|
|
||||||
get => _node.SelectedAnimationIndex;
|
|
||||||
set => _node.SelectedAnimationIndex = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void SetLocation(int index, Float2 location)
|
|
||||||
{
|
|
||||||
var dataA = (Float4)_node.Values[4 + index * 2];
|
|
||||||
var ranges = (Float4)_node.Values[0];
|
|
||||||
|
|
||||||
dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y);
|
|
||||||
dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W);
|
|
||||||
|
|
||||||
_node.Values[4 + index * 2] = dataA;
|
|
||||||
_node.Surface.MarkAsEdited();
|
|
||||||
|
|
||||||
_node.UpdateUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||||
@@ -795,11 +984,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
};
|
};
|
||||||
_animationY.ValueChanged += OnAnimationYChanged;
|
_animationY.ValueChanged += OnAnimationYChanged;
|
||||||
|
|
||||||
_editor = new Editor(this,
|
var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f;
|
||||||
FlaxEditor.Surface.Constants.NodeMarginX,
|
_editor = new BlendPointsEditor(this, true, FlaxEditor.Surface.Constants.NodeMarginX, _animationY.Bottom + 4.0f, size, size);
|
||||||
_animationY.Bottom + 4.0f,
|
|
||||||
Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f,
|
|
||||||
120.0f);
|
|
||||||
_editor.Parent = this;
|
_editor.Parent = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,7 +1020,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
||||||
{
|
{
|
||||||
base.UpdateUI(selectedIndex, isValid, ref data0, ref data1);
|
base.UpdateUI(selectedIndex, isValid, ref data0, ref data1);
|
||||||
|
|
||||||
|
|||||||
@@ -621,7 +621,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Create = (id, context, arch, groupArch) => new MultiBlend1D(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new MultiBlend1D(id, context, arch, groupArch),
|
||||||
Title = "Multi Blend 1D",
|
Title = "Multi Blend 1D",
|
||||||
Description = "Animation blending in 1D",
|
Description = "Animation blending in 1D",
|
||||||
Flags = NodeFlags.AnimGraph,
|
Flags = NodeFlags.AnimGraph | NodeFlags.VariableValuesSize,
|
||||||
Size = new Float2(420, 300),
|
Size = new Float2(420, 300),
|
||||||
DefaultValues = new object[]
|
DefaultValues = new object[]
|
||||||
{
|
{
|
||||||
@@ -633,19 +633,6 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
|
|
||||||
// Per blend sample data
|
// Per blend sample data
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
},
|
},
|
||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
@@ -658,10 +645,10 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 3, 3),
|
NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 3, 3),
|
||||||
|
|
||||||
// Axis X
|
// Axis X
|
||||||
NodeElementArchetype.Factory.Input(4, "X", true, typeof(float), 4),
|
NodeElementArchetype.Factory.Input(3, "X", true, typeof(float), 4),
|
||||||
NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY, "(min: max: )"),
|
NodeElementArchetype.Factory.Text(30, 3 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"),
|
||||||
NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY, 0, 0),
|
NodeElementArchetype.Factory.Float(60, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 0),
|
||||||
NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY, 0, 1),
|
NodeElementArchetype.Factory.Float(145, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 1),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
@@ -670,8 +657,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Create = (id, context, arch, groupArch) => new MultiBlend2D(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new MultiBlend2D(id, context, arch, groupArch),
|
||||||
Title = "Multi Blend 2D",
|
Title = "Multi Blend 2D",
|
||||||
Description = "Animation blending in 2D",
|
Description = "Animation blending in 2D",
|
||||||
Flags = NodeFlags.AnimGraph,
|
Flags = NodeFlags.AnimGraph | NodeFlags.VariableValuesSize,
|
||||||
Size = new Float2(420, 320),
|
Size = new Float2(420, 620),
|
||||||
DefaultValues = new object[]
|
DefaultValues = new object[]
|
||||||
{
|
{
|
||||||
// Node data
|
// Node data
|
||||||
@@ -682,19 +669,6 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
|
|
||||||
// Per blend sample data
|
// Per blend sample data
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
new Float4(0, 0, 0, 1.0f), Guid.Empty,
|
|
||||||
},
|
},
|
||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
@@ -707,16 +681,16 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 3, 3),
|
NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 3, 3),
|
||||||
|
|
||||||
// Axis X
|
// Axis X
|
||||||
NodeElementArchetype.Factory.Input(4, "X", true, typeof(float), 4),
|
NodeElementArchetype.Factory.Input(3, "X", true, typeof(float), 4),
|
||||||
NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY, "(min: max: )"),
|
NodeElementArchetype.Factory.Text(30, 3 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"),
|
||||||
NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY, 0, 0),
|
NodeElementArchetype.Factory.Float(60, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 0),
|
||||||
NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY, 0, 1),
|
NodeElementArchetype.Factory.Float(145, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 1),
|
||||||
|
|
||||||
// Axis Y
|
// Axis Y
|
||||||
NodeElementArchetype.Factory.Input(5, "Y", true, typeof(float), 5),
|
NodeElementArchetype.Factory.Input(4, "Y", true, typeof(float), 5),
|
||||||
NodeElementArchetype.Factory.Text(30, 5 * Surface.Constants.LayoutOffsetY, "(min: max: )"),
|
NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"),
|
||||||
NodeElementArchetype.Factory.Float(60, 5 * Surface.Constants.LayoutOffsetY, 0, 2),
|
NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY + 2, 0, 2),
|
||||||
NodeElementArchetype.Factory.Float(145, 5 * Surface.Constants.LayoutOffsetY, 0, 3),
|
NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY + 2, 0, 3),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
|
|||||||
@@ -104,6 +104,16 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnTypeDisposing(ScriptType type)
|
||||||
|
{
|
||||||
|
if (_type == type && !IsDisposing)
|
||||||
|
{
|
||||||
|
// Turn into missing script
|
||||||
|
_type = ScriptType.Null;
|
||||||
|
Instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnLoaded(SurfaceNodeActions action)
|
public override void OnLoaded(SurfaceNodeActions action)
|
||||||
{
|
{
|
||||||
base.OnLoaded(action);
|
base.OnLoaded(action);
|
||||||
@@ -113,6 +123,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_type = TypeUtils.GetType(typeName);
|
_type = TypeUtils.GetType(typeName);
|
||||||
if (_type != null)
|
if (_type != null)
|
||||||
{
|
{
|
||||||
|
_type.TrackLifetime(OnTypeDisposing);
|
||||||
TooltipText = Editor.Instance.CodeDocs.GetTooltip(_type);
|
TooltipText = Editor.Instance.CodeDocs.GetTooltip(_type);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
BehaviorTreeGraph = 1024,
|
BehaviorTreeGraph = 1024,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Node can have different amount of items in values array.
|
||||||
|
/// </summary>
|
||||||
|
VariableValuesSize = 2048,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Node can be used in the all visual graphs.
|
/// Node can be used in the all visual graphs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -951,15 +951,20 @@ namespace FlaxEditor.Surface
|
|||||||
{
|
{
|
||||||
if (_isDuringValuesEditing || !Surface.CanEdit)
|
if (_isDuringValuesEditing || !Surface.CanEdit)
|
||||||
return;
|
return;
|
||||||
|
if (values == null || Values == null)
|
||||||
if (values == null || Values == null || values.Length != Values.Length)
|
throw new ArgumentException();
|
||||||
|
bool resize = values.Length != Values.Length;
|
||||||
|
if (resize && (Archetype.Flags & NodeFlags.VariableValuesSize) == 0)
|
||||||
throw new ArgumentException();
|
throw new ArgumentException();
|
||||||
|
|
||||||
_isDuringValuesEditing = true;
|
_isDuringValuesEditing = true;
|
||||||
|
|
||||||
var before = Surface.Undo != null ? (object[])Values.Clone() : null;
|
var before = Surface.Undo != null ? (object[])Values.Clone() : null;
|
||||||
|
|
||||||
Array.Copy(values, Values, values.Length);
|
if (resize)
|
||||||
|
Values = (object[])values.Clone();
|
||||||
|
else
|
||||||
|
Array.Copy(values, Values, values.Length);
|
||||||
OnValuesChanged();
|
OnValuesChanged();
|
||||||
Surface.MarkAsEdited(graphEdited);
|
Surface.MarkAsEdited(graphEdited);
|
||||||
|
|
||||||
@@ -1057,6 +1062,20 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDown(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
|
||||||
|
return true;
|
||||||
|
if (button == MouseButton.Right)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
{
|
{
|
||||||
@@ -1064,13 +1083,10 @@ namespace FlaxEditor.Surface
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Close
|
// Close
|
||||||
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0)
|
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
|
||||||
{
|
{
|
||||||
if (_closeButtonRect.Contains(ref location))
|
Surface.Delete(this);
|
||||||
{
|
return true;
|
||||||
Surface.Delete(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secondary Context Menu
|
// Secondary Context Menu
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace FlaxEditor.Surface.Undo
|
|||||||
private ContextHandle _context;
|
private ContextHandle _context;
|
||||||
private readonly uint _nodeId;
|
private readonly uint _nodeId;
|
||||||
private readonly bool _graphEdited;
|
private readonly bool _graphEdited;
|
||||||
|
private readonly bool _resize;
|
||||||
private object[] _before;
|
private object[] _before;
|
||||||
private object[] _after;
|
private object[] _after;
|
||||||
|
|
||||||
@@ -23,7 +24,8 @@ namespace FlaxEditor.Surface.Undo
|
|||||||
throw new ArgumentNullException(nameof(before));
|
throw new ArgumentNullException(nameof(before));
|
||||||
if (node?.Values == null)
|
if (node?.Values == null)
|
||||||
throw new ArgumentNullException(nameof(node));
|
throw new ArgumentNullException(nameof(node));
|
||||||
if (before.Length != node.Values.Length)
|
_resize = before.Length != node.Values.Length;
|
||||||
|
if (_resize && (node.Archetype.Flags & NodeFlags.VariableValuesSize) == 0)
|
||||||
throw new ArgumentException(nameof(before));
|
throw new ArgumentException(nameof(before));
|
||||||
|
|
||||||
_surface = node.Surface;
|
_surface = node.Surface;
|
||||||
@@ -48,7 +50,10 @@ namespace FlaxEditor.Surface.Undo
|
|||||||
throw new Exception("Missing node.");
|
throw new Exception("Missing node.");
|
||||||
|
|
||||||
node.SetIsDuringValuesEditing(true);
|
node.SetIsDuringValuesEditing(true);
|
||||||
Array.Copy(_after, node.Values, _after.Length);
|
if (_resize)
|
||||||
|
node.Values = (object[])_after.Clone();
|
||||||
|
else
|
||||||
|
Array.Copy(_after, node.Values, _after.Length);
|
||||||
node.OnValuesChanged();
|
node.OnValuesChanged();
|
||||||
context.MarkAsModified(_graphEdited);
|
context.MarkAsModified(_graphEdited);
|
||||||
node.SetIsDuringValuesEditing(false);
|
node.SetIsDuringValuesEditing(false);
|
||||||
@@ -65,7 +70,10 @@ namespace FlaxEditor.Surface.Undo
|
|||||||
throw new Exception("Missing node.");
|
throw new Exception("Missing node.");
|
||||||
|
|
||||||
node.SetIsDuringValuesEditing(true);
|
node.SetIsDuringValuesEditing(true);
|
||||||
Array.Copy(_before, node.Values, _before.Length);
|
if (_resize)
|
||||||
|
node.Values = (object[])_before.Clone();
|
||||||
|
else
|
||||||
|
Array.Copy(_before, node.Values, _before.Length);
|
||||||
node.OnValuesChanged();
|
node.OnValuesChanged();
|
||||||
context.MarkAsModified(_graphEdited);
|
context.MarkAsModified(_graphEdited);
|
||||||
node.SetIsDuringValuesEditing(false);
|
node.SetIsDuringValuesEditing(false);
|
||||||
|
|||||||
@@ -630,6 +630,15 @@ namespace FlaxEditor.Surface
|
|||||||
stream.ReadCommonValue(ref node.Values[j]);
|
stream.ReadCommonValue(ref node.Values[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
|
||||||
|
{
|
||||||
|
node.Values = new object[valuesCnt];
|
||||||
|
for (int j = firstValueReadIdx; j < valuesCnt; j++)
|
||||||
|
{
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
stream.ReadCommonValue(ref node.Values[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
|
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
|
||||||
@@ -795,6 +804,12 @@ namespace FlaxEditor.Surface
|
|||||||
for (int j = firstValueReadIdx; j < valuesCnt; j++)
|
for (int j = firstValueReadIdx; j < valuesCnt; j++)
|
||||||
node.Values[j] = stream.ReadVariant();
|
node.Values[j] = stream.ReadVariant();
|
||||||
}
|
}
|
||||||
|
else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
|
||||||
|
{
|
||||||
|
node.Values = new object[valuesCnt];
|
||||||
|
for (int j = firstValueReadIdx; j < valuesCnt; j++)
|
||||||
|
node.Values[j] = stream.ReadVariant();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
|
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
|
||||||
|
|||||||
@@ -120,9 +120,9 @@ namespace FlaxEditor.Utilities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the creates scenes for the simulation.
|
/// Deletes the creates scenes for the simulation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void DeletedScenes()
|
public void UnloadScenes()
|
||||||
{
|
{
|
||||||
Profiler.BeginEvent("DuplicateScenes.DeletedScenes");
|
Profiler.BeginEvent("DuplicateScenes.UnloadScenes");
|
||||||
Editor.Log("Restoring scene data");
|
Editor.Log("Restoring scene data");
|
||||||
|
|
||||||
// TODO: here we can keep changes for actors marked to keep their state after simulation
|
// TODO: here we can keep changes for actors marked to keep their state after simulation
|
||||||
@@ -134,6 +134,8 @@ namespace FlaxEditor.Utilities
|
|||||||
throw new Exception("Failed to unload scenes.");
|
throw new Exception("Failed to unload scenes.");
|
||||||
}
|
}
|
||||||
FlaxEngine.Scripting.FlushRemovedObjects();
|
FlaxEngine.Scripting.FlushRemovedObjects();
|
||||||
|
Editor.WipeOutLeftoverSceneObjects();
|
||||||
|
|
||||||
Profiler.EndEvent();
|
Profiler.EndEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,12 +210,17 @@ namespace FlaxEditor.Utilities
|
|||||||
/// <returns>The duplicated value.</returns>
|
/// <returns>The duplicated value.</returns>
|
||||||
internal static object CloneValue(object value)
|
internal static object CloneValue(object value)
|
||||||
{
|
{
|
||||||
|
// For object references just clone it
|
||||||
|
if (value is FlaxEngine.Object)
|
||||||
|
return value;
|
||||||
|
|
||||||
// For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor
|
// For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor
|
||||||
if (value != null && (!value.GetType().IsValueType || !value.GetType().IsClass))
|
if (value != null && (!value.GetType().IsValueType || !value.GetType().IsClass))
|
||||||
{
|
{
|
||||||
var json = JsonSerializer.Serialize(value);
|
var json = JsonSerializer.Serialize(value);
|
||||||
value = JsonSerializer.Deserialize(json, value.GetType());
|
value = JsonSerializer.Deserialize(json, value.GetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,12 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
_hasUILinked = true;
|
_hasUILinked = true;
|
||||||
}
|
}
|
||||||
else if (_uiControlLinked != null)
|
else if (_uiControlLinked != null)
|
||||||
|
{
|
||||||
|
if (_uiControlLinked.Control != null &&
|
||||||
|
_uiControlLinked.Control.Parent == null)
|
||||||
|
_uiControlLinked.Control.Parent = _uiParentLink;
|
||||||
_hasUILinked = true;
|
_hasUILinked = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LinkCanvas(Actor actor)
|
private void LinkCanvas(Actor actor)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public:
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
FORCE_INLINE bool CanUpdateModel(AnimatedModel* animatedModel)
|
FORCE_INLINE bool CanUpdateModel(const AnimatedModel* animatedModel)
|
||||||
{
|
{
|
||||||
auto skinnedModel = animatedModel->SkinnedModel.Get();
|
auto skinnedModel = animatedModel->SkinnedModel.Get();
|
||||||
auto animGraph = animatedModel->AnimationGraph.Get();
|
auto animGraph = animatedModel->AnimationGraph.Get();
|
||||||
|
|||||||
@@ -117,11 +117,11 @@ void InstanceDataBucketInit(AnimGraphInstanceData::Bucket& bucket)
|
|||||||
bucket.InstanceData.Init = true;
|
bucket.InstanceData.Init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
|
bool SortMultiBlend1D(const ANIM_GRAPH_MULTI_BLEND_INDEX& a, const ANIM_GRAPH_MULTI_BLEND_INDEX& b, AnimGraphNode* n)
|
||||||
{
|
{
|
||||||
// Sort items by X location from the lowest to the highest
|
// Sort items by X location from the lowest to the highest
|
||||||
const auto aX = a == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? MAX_float : n->Values[4 + a * 2].AsFloat4().X;
|
const auto aX = a == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + a * 2].AsFloat4().X;
|
||||||
const auto bX = b == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? MAX_float : n->Values[4 + b * 2].AsFloat4().X;
|
const auto bX = b == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + b * 2].AsFloat4().X;
|
||||||
return aX < bX;
|
return aX < bX;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +133,6 @@ bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
|
|||||||
bool AnimGraphBase::onNodeLoaded(Node* n)
|
bool AnimGraphBase::onNodeLoaded(Node* n)
|
||||||
{
|
{
|
||||||
((AnimGraphNode*)n)->Graph = _graph;
|
((AnimGraphNode*)n)->Graph = _graph;
|
||||||
|
|
||||||
// Check if this node needs a state container
|
|
||||||
switch (n->GroupID)
|
switch (n->GroupID)
|
||||||
{
|
{
|
||||||
// Tools
|
// Tools
|
||||||
@@ -163,53 +161,51 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
|
|||||||
// Animation
|
// Animation
|
||||||
case 2:
|
case 2:
|
||||||
ADD_BUCKET(AnimationBucketInit);
|
ADD_BUCKET(AnimationBucketInit);
|
||||||
|
n->Assets.Resize(1);
|
||||||
n->Assets[0] = (Asset*)Content::LoadAsync<Animation>((Guid)n->Values[0]);
|
n->Assets[0] = (Asset*)Content::LoadAsync<Animation>((Guid)n->Values[0]);
|
||||||
break;
|
break;
|
||||||
// Blend with Mask
|
// Blend with Mask
|
||||||
case 11:
|
case 11:
|
||||||
|
n->Assets.Resize(1);
|
||||||
n->Assets[0] = (Asset*)Content::LoadAsync<SkeletonMask>((Guid)n->Values[1]);
|
n->Assets[0] = (Asset*)Content::LoadAsync<SkeletonMask>((Guid)n->Values[1]);
|
||||||
break;
|
break;
|
||||||
// Multi Blend 1D
|
// Multi Blend 1D
|
||||||
case 12:
|
case 12:
|
||||||
{
|
|
||||||
ADD_BUCKET(MultiBlendBucketInit);
|
ADD_BUCKET(MultiBlendBucketInit);
|
||||||
|
n->Data.MultiBlend1D.Count = (ANIM_GRAPH_MULTI_BLEND_INDEX)((n->Values.Count() - 4) / 2); // 4 node values + 2 per blend point
|
||||||
n->Data.MultiBlend1D.Length = -1;
|
n->Data.MultiBlend1D.Length = -1;
|
||||||
const Float4 range = n->Values[0].AsFloat4();
|
n->Data.MultiBlend1D.IndicesSorted = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX) * n->Data.MultiBlend1D.Count);
|
||||||
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
|
n->Assets.Resize(n->Data.MultiBlend1D.Count);
|
||||||
|
for (int32 i = 0; i < n->Data.MultiBlend1D.Count; i++)
|
||||||
{
|
{
|
||||||
n->Assets[i] = Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
|
n->Assets[i] = Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
|
||||||
n->Data.MultiBlend1D.IndicesSorted[i] = n->Assets[i] ? i : ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS;
|
n->Data.MultiBlend1D.IndicesSorted[i] = (ANIM_GRAPH_MULTI_BLEND_INDEX)(n->Assets[i] ? i : ANIM_GRAPH_MULTI_BLEND_INVALID);
|
||||||
}
|
}
|
||||||
Sorting::SortArray(n->Data.MultiBlend1D.IndicesSorted, ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS, &SortMultiBlend1D, n);
|
Sorting::SortArray(n->Data.MultiBlend1D.IndicesSorted, n->Data.MultiBlend1D.Count, &SortMultiBlend1D, n);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
// Multi Blend 2D
|
// Multi Blend 2D
|
||||||
case 13:
|
case 13:
|
||||||
{
|
{
|
||||||
ADD_BUCKET(MultiBlendBucketInit);
|
ADD_BUCKET(MultiBlendBucketInit);
|
||||||
|
n->Data.MultiBlend1D.Count = (ANIM_GRAPH_MULTI_BLEND_INDEX)((n->Values.Count() - 4) / 2); // 4 node values + 2 per blend point
|
||||||
n->Data.MultiBlend2D.Length = -1;
|
n->Data.MultiBlend2D.Length = -1;
|
||||||
|
|
||||||
// Get blend points locations
|
// Get blend points locations
|
||||||
Array<Float2, FixedAllocation<ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS + 3>> vertices;
|
Array<Float2> vertices;
|
||||||
byte vertexIndexToAnimIndex[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS];
|
Array<ANIM_GRAPH_MULTI_BLEND_INDEX> vertexToAnim;
|
||||||
const Float4 range = n->Values[0].AsFloat4();
|
n->Assets.Resize(n->Data.MultiBlend1D.Count);
|
||||||
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
|
for (int32 i = 0; i < n->Data.MultiBlend1D.Count; i++)
|
||||||
{
|
{
|
||||||
n->Assets[i] = (Asset*)Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
|
n->Assets[i] = Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
|
||||||
if (n->Assets[i])
|
if (n->Assets[i])
|
||||||
{
|
{
|
||||||
const int32 vertexIndex = vertices.Count();
|
|
||||||
vertexIndexToAnimIndex[vertexIndex] = i;
|
|
||||||
vertices.Add(Float2(n->Values[i * 2 + 4].AsFloat4()));
|
vertices.Add(Float2(n->Values[i * 2 + 4].AsFloat4()));
|
||||||
}
|
vertexToAnim.Add((ANIM_GRAPH_MULTI_BLEND_INDEX)i);
|
||||||
else
|
|
||||||
{
|
|
||||||
vertexIndexToAnimIndex[i] = -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triangulate
|
// Triangulate
|
||||||
Array<Delaunay2D::Triangle, FixedAllocation<ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS>> triangles;
|
Array<Delaunay2D::Triangle> triangles;
|
||||||
Delaunay2D::Triangulate(vertices, triangles);
|
Delaunay2D::Triangulate(vertices, triangles);
|
||||||
if (triangles.Count() == 0)
|
if (triangles.Count() == 0)
|
||||||
{
|
{
|
||||||
@@ -227,15 +223,14 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store triangles vertices indices (map the back to the anim node slots)
|
// Store triangles vertices indices (map the back to the anim node slots)
|
||||||
for (int32 i = 0; i < triangles.Count(); i++)
|
n->Data.MultiBlend2D.TrianglesCount = triangles.Count();
|
||||||
|
n->Data.MultiBlend2D.Triangles = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(triangles.Count() * 3 - sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX));
|
||||||
|
for (int32 i = 0, t = 0; i < triangles.Count(); i++)
|
||||||
{
|
{
|
||||||
n->Data.MultiBlend2D.TrianglesP0[i] = vertexIndexToAnimIndex[triangles[i].Indices[0]];
|
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[0]];
|
||||||
n->Data.MultiBlend2D.TrianglesP1[i] = vertexIndexToAnimIndex[triangles[i].Indices[1]];
|
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[1]];
|
||||||
n->Data.MultiBlend2D.TrianglesP2[i] = vertexIndexToAnimIndex[triangles[i].Indices[2]];
|
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[2]];
|
||||||
}
|
}
|
||||||
if (triangles.Count() < ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS)
|
|
||||||
n->Data.MultiBlend2D.TrianglesP0[triangles.Count()] = ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Blend Pose
|
// Blend Pose
|
||||||
@@ -307,6 +302,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
|
|||||||
data.Graph = nullptr;
|
data.Graph = nullptr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
n->Assets.Resize(1);
|
||||||
n->Assets[0] = function;
|
n->Assets[0] = function;
|
||||||
|
|
||||||
// Load the graph
|
// Load the graph
|
||||||
@@ -384,6 +380,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
|
|||||||
void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Value& transitionsData)
|
void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Value& transitionsData)
|
||||||
{
|
{
|
||||||
int32 validTransitions = 0;
|
int32 validTransitions = 0;
|
||||||
|
data.Transitions = nullptr;
|
||||||
if (transitionsData.Type == VariantType::Blob && transitionsData.AsBlob.Length)
|
if (transitionsData.Type == VariantType::Blob && transitionsData.AsBlob.Length)
|
||||||
{
|
{
|
||||||
MemoryReadStream stream((byte*)transitionsData.AsBlob.Data, transitionsData.AsBlob.Length);
|
MemoryReadStream stream((byte*)transitionsData.AsBlob.Data, transitionsData.AsBlob.Length);
|
||||||
@@ -398,8 +395,11 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val
|
|||||||
|
|
||||||
int32 transitionsCount;
|
int32 transitionsCount;
|
||||||
stream.ReadInt32(&transitionsCount);
|
stream.ReadInt32(&transitionsCount);
|
||||||
|
if (transitionsCount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
StateTransitions.EnsureCapacity(StateTransitions.Count() + transitionsCount);
|
StateTransitions.EnsureCapacity(StateTransitions.Count() + transitionsCount);
|
||||||
|
data.Transitions = (uint16*)Allocator::Allocate(transitionsCount * sizeof(uint16));
|
||||||
|
|
||||||
AnimGraphStateTransition transition;
|
AnimGraphStateTransition transition;
|
||||||
for (int32 i = 0; i < transitionsCount; i++)
|
for (int32 i = 0; i < transitionsCount; i++)
|
||||||
@@ -434,7 +434,6 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val
|
|||||||
// Skip disabled transitions
|
// Skip disabled transitions
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ruleSize != 0)
|
if (ruleSize != 0)
|
||||||
{
|
{
|
||||||
transition.RuleGraph = LoadSubGraph(ruleBytes, ruleSize, TEXT("Rule"));
|
transition.RuleGraph = LoadSubGraph(ruleBytes, ruleSize, TEXT("Rule"));
|
||||||
@@ -444,25 +443,19 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transition.Destination == nullptr)
|
if (transition.Destination == nullptr)
|
||||||
{
|
{
|
||||||
LOG(Warning, "Missing target node for the state machine transition.");
|
LOG(Warning, "Missing target node for the state machine transition.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validTransitions == ANIM_GRAPH_MAX_STATE_TRANSITIONS)
|
|
||||||
{
|
|
||||||
LOG(Warning, "State uses too many transitions.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Transitions[validTransitions++] = (uint16)StateTransitions.Count();
|
data.Transitions[validTransitions++] = (uint16)StateTransitions.Count();
|
||||||
StateTransitions.Add(transition);
|
StateTransitions.Add(transition);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (validTransitions != ANIM_GRAPH_MAX_STATE_TRANSITIONS)
|
// Last entry is invalid to indicate end
|
||||||
data.Transitions[validTransitions] = AnimGraphNode::StateData::InvalidTransitionIndex;
|
data.Transitions[validTransitions] = AnimGraphNode::StateData::InvalidTransitionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
// Release data to don't use that memory
|
// Release data to don't use that memory
|
||||||
transitionsData = AnimGraphExecutor::Value::Null;
|
transitionsData = AnimGraphExecutor::Value::Null;
|
||||||
|
|||||||
@@ -102,6 +102,34 @@ AnimGraphInstanceData::OutgoingEvent AnimGraphInstanceData::ActiveEvent::End(Ani
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AnimGraphNode::~AnimGraphNode()
|
||||||
|
{
|
||||||
|
// Free allocated memory
|
||||||
|
switch (GroupID)
|
||||||
|
{
|
||||||
|
// Animation
|
||||||
|
case 9:
|
||||||
|
switch (TypeID)
|
||||||
|
{
|
||||||
|
// Multi Blend 1D
|
||||||
|
case 12:
|
||||||
|
Allocator::Free(Data.MultiBlend1D.IndicesSorted);
|
||||||
|
break;
|
||||||
|
// Multi Blend 2D
|
||||||
|
case 13:
|
||||||
|
Allocator::Free(Data.MultiBlend2D.Triangles);
|
||||||
|
break;
|
||||||
|
// State
|
||||||
|
case 20:
|
||||||
|
// Any State
|
||||||
|
case 34:
|
||||||
|
Allocator::Free(Data.State.Transitions);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AnimGraphImpulse* AnimGraphNode::GetNodes(AnimGraphExecutor* executor)
|
AnimGraphImpulse* AnimGraphNode::GetNodes(AnimGraphExecutor* executor)
|
||||||
{
|
{
|
||||||
auto& context = *AnimGraphExecutor::Context.Get();
|
auto& context = *AnimGraphExecutor::Context.Get();
|
||||||
|
|||||||
@@ -13,9 +13,8 @@
|
|||||||
#define ANIM_GRAPH_PARAM_BASE_MODEL_ID Guid(1000, 0, 0, 0)
|
#define ANIM_GRAPH_PARAM_BASE_MODEL_ID Guid(1000, 0, 0, 0)
|
||||||
|
|
||||||
#define ANIM_GRAPH_IS_VALID_PTR(value) (value.Type.Type == VariantType::Pointer && value.AsPointer != nullptr)
|
#define ANIM_GRAPH_IS_VALID_PTR(value) (value.Type.Type == VariantType::Pointer && value.AsPointer != nullptr)
|
||||||
#define ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS 14
|
#define ANIM_GRAPH_MULTI_BLEND_INDEX byte
|
||||||
#define ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS 32
|
#define ANIM_GRAPH_MULTI_BLEND_INVALID 0xff
|
||||||
#define ANIM_GRAPH_MAX_STATE_TRANSITIONS 64
|
|
||||||
#define ANIM_GRAPH_MAX_CALL_STACK 100
|
#define ANIM_GRAPH_MAX_CALL_STACK 100
|
||||||
#define ANIM_GRAPH_MAX_EVENTS 64
|
#define ANIM_GRAPH_MAX_EVENTS 64
|
||||||
|
|
||||||
@@ -427,61 +426,31 @@ struct AnimGraphTransitionData
|
|||||||
float Length;
|
float Length;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimGraphBox : public VisjectGraphBox
|
typedef VisjectGraphBox AnimGraphBox;
|
||||||
{
|
|
||||||
public:
|
|
||||||
AnimGraphBox()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimGraphBox(AnimGraphNode* parent, byte id, const VariantType::Types type)
|
|
||||||
: VisjectGraphBox(parent, id, type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimGraphBox(AnimGraphNode* parent, byte id, const VariantType& type)
|
|
||||||
: VisjectGraphBox(parent, id, type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class AnimGraphNode : public VisjectGraphNode<AnimGraphBox>
|
class AnimGraphNode : public VisjectGraphNode<AnimGraphBox>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct MultiBlend1DData
|
struct MultiBlend1DData
|
||||||
{
|
{
|
||||||
/// <summary>
|
// Amount of blend points.
|
||||||
/// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
|
ANIM_GRAPH_MULTI_BLEND_INDEX Count;
|
||||||
/// </summary>
|
// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
|
||||||
float Length;
|
float Length;
|
||||||
|
// The indices of the animations to blend. Sorted from the lowest X to the highest X. Contains only valid used animations. Unused items are using index ANIM_GRAPH_MULTI_BLEND_INVALID which is invalid.
|
||||||
/// <summary>
|
ANIM_GRAPH_MULTI_BLEND_INDEX* IndicesSorted;
|
||||||
/// The indices of the animations to blend. Sorted from the lowest X to the highest X. Contains only valid used animations. Unused items are using index ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS which is invalid.
|
|
||||||
/// </summary>
|
|
||||||
byte IndicesSorted[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MultiBlend2DData
|
struct MultiBlend2DData
|
||||||
{
|
{
|
||||||
/// <summary>
|
// Amount of blend points.
|
||||||
/// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
|
ANIM_GRAPH_MULTI_BLEND_INDEX Count;
|
||||||
/// </summary>
|
// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
|
||||||
float Length;
|
float Length;
|
||||||
|
// Amount of triangles.
|
||||||
/// <summary>
|
int32 TrianglesCount;
|
||||||
/// Cached triangles vertices (vertex 0). Contains list of indices for triangles to use for blending.
|
// Cached triangles vertices (3 bytes per triangle). Contains list of indices for triangles to use for blending.
|
||||||
/// </summary>
|
ANIM_GRAPH_MULTI_BLEND_INDEX* Triangles;
|
||||||
byte TrianglesP0[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cached triangles vertices (vertex 1). Contains list of indices for triangles to use for blending.
|
|
||||||
/// </summary>
|
|
||||||
byte TrianglesP1[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cached triangles vertices (vertex 2). Contains list of indices for triangles to use for blending.
|
|
||||||
/// </summary>
|
|
||||||
byte TrianglesP2[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StateMachineData
|
struct StateMachineData
|
||||||
@@ -494,15 +463,11 @@ public:
|
|||||||
|
|
||||||
struct StateBaseData
|
struct StateBaseData
|
||||||
{
|
{
|
||||||
/// <summary>
|
// The invalid transition valid used in Transitions to indicate invalid transition linkage.
|
||||||
/// The invalid transition valid used in Transitions to indicate invalid transition linkage.
|
|
||||||
/// </summary>
|
|
||||||
const static uint16 InvalidTransitionIndex = MAX_uint16;
|
const static uint16 InvalidTransitionIndex = MAX_uint16;
|
||||||
|
|
||||||
/// <summary>
|
// The outgoing transitions from this state to the other states. Each array item contains index of the transition data from the state node graph transitions cache. Value InvalidTransitionIndex is used for last transition to indicate the transitions amount.
|
||||||
/// The outgoing transitions from this state to the other states. Each array item contains index of the transition data from the state node graph transitions cache. Value InvalidTransitionIndex is used for last transition to indicate the transitions amount.
|
uint16* Transitions;
|
||||||
/// </summary>
|
|
||||||
uint16 Transitions[ANIM_GRAPH_MAX_STATE_TRANSITIONS];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StateData : StateBaseData
|
struct StateData : StateBaseData
|
||||||
@@ -598,12 +563,14 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~AnimGraphNode();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the per-node node transformations cache (cached).
|
/// Gets the per-node node transformations cache (cached).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="executor">The Graph execution context.</param>
|
/// <param name="executor">The Graph execution context.</param>
|
||||||
/// <returns>The modes data.</returns>
|
/// <returns>Nodes data.</returns>
|
||||||
AnimGraphImpulse* GetNodes(AnimGraphExecutor* executor);
|
AnimGraphImpulse* GetNodes(AnimGraphExecutor* executor);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -800,7 +767,7 @@ struct AnimGraphContext
|
|||||||
bool StackOverFlow;
|
bool StackOverFlow;
|
||||||
Array<VisjectExecutor::Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> CallStack;
|
Array<VisjectExecutor::Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> CallStack;
|
||||||
Array<VisjectExecutor::Graph*, FixedAllocation<32>> GraphStack;
|
Array<VisjectExecutor::Graph*, FixedAllocation<32>> GraphStack;
|
||||||
Array<uint32, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK> > NodePath;
|
Array<uint32, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> NodePath;
|
||||||
Dictionary<VisjectExecutor::Node*, VisjectExecutor::Graph*> Functions;
|
Dictionary<VisjectExecutor::Node*, VisjectExecutor::Graph*> Functions;
|
||||||
ChunkedArray<AnimGraphImpulse, 256> PoseCache;
|
ChunkedArray<AnimGraphImpulse, 256> PoseCache;
|
||||||
int32 PoseCacheSize;
|
int32 PoseCacheSize;
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
|||||||
{
|
{
|
||||||
// Per-channel bit to indicate which channels were used by nested
|
// Per-channel bit to indicate which channels were used by nested
|
||||||
usedNodesThis.Resize(nodes->Nodes.Count());
|
usedNodesThis.Resize(nodes->Nodes.Count());
|
||||||
|
usedNodesThis.SetAll(false);
|
||||||
usedNodes = &usedNodesThis;
|
usedNodes = &usedNodesThis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,11 +287,11 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
|||||||
SkinnedModel::SkeletonMapping sourceMapping;
|
SkinnedModel::SkeletonMapping sourceMapping;
|
||||||
if (retarget)
|
if (retarget)
|
||||||
sourceMapping = _graph.BaseModel->GetSkeletonMapping(mapping.SourceSkeleton);
|
sourceMapping = _graph.BaseModel->GetSkeletonMapping(mapping.SourceSkeleton);
|
||||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
for (int32 nodeIndex = 0; nodeIndex < nodes->Nodes.Count(); nodeIndex++)
|
||||||
{
|
{
|
||||||
const int32 nodeToChannel = mapping.NodesMapping[i];
|
const int32 nodeToChannel = mapping.NodesMapping[nodeIndex];
|
||||||
Transform& dstNode = nodes->Nodes[i];
|
Transform& dstNode = nodes->Nodes[nodeIndex];
|
||||||
Transform srcNode = emptyNodes->Nodes[i];
|
Transform srcNode = emptyNodes->Nodes[nodeIndex];
|
||||||
if (nodeToChannel != -1)
|
if (nodeToChannel != -1)
|
||||||
{
|
{
|
||||||
// Calculate the animated node transformation
|
// Calculate the animated node transformation
|
||||||
@@ -299,14 +300,14 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
|||||||
// Optionally retarget animation into the skeleton used by the Anim Graph
|
// Optionally retarget animation into the skeleton used by the Anim Graph
|
||||||
if (retarget)
|
if (retarget)
|
||||||
{
|
{
|
||||||
RetargetSkeletonNode(mapping.SourceSkeleton->Skeleton, mapping.TargetSkeleton->Skeleton, sourceMapping, srcNode, i);
|
RetargetSkeletonNode(mapping.SourceSkeleton->Skeleton, mapping.TargetSkeleton->Skeleton, sourceMapping, srcNode, nodeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark node as used
|
// Mark node as used
|
||||||
if (usedNodes)
|
if (usedNodes)
|
||||||
usedNodes->Set(i, true);
|
usedNodes->Set(nodeIndex, true);
|
||||||
}
|
}
|
||||||
else if (usedNodes && usedNodes != &usedNodesThis)
|
else if (usedNodes && (usedNodes != &usedNodesThis || usedNodes->Get(nodeIndex)))
|
||||||
{
|
{
|
||||||
// Skip for nested animations so other one or top-level anim will update remaining nodes
|
// Skip for nested animations so other one or top-level anim will update remaining nodes
|
||||||
continue;
|
continue;
|
||||||
@@ -585,7 +586,7 @@ AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphCon
|
|||||||
AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, const AnimGraphNode::StateBaseData& stateData, AnimGraphNode* state, AnimGraphNode* ignoreState)
|
AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, const AnimGraphNode::StateBaseData& stateData, AnimGraphNode* state, AnimGraphNode* ignoreState)
|
||||||
{
|
{
|
||||||
int32 transitionIndex = 0;
|
int32 transitionIndex = 0;
|
||||||
while (transitionIndex < ANIM_GRAPH_MAX_STATE_TRANSITIONS && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex)
|
while (stateData.Transitions && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex)
|
||||||
{
|
{
|
||||||
const uint16 idx = stateData.Transitions[transitionIndex];
|
const uint16 idx = stateData.Transitions[transitionIndex];
|
||||||
ASSERT(idx < stateMachineData.Graph->StateTransitions.Count());
|
ASSERT(idx < stateMachineData.Graph->StateTransitions.Count());
|
||||||
@@ -673,19 +674,20 @@ void ComputeMultiBlendLength(float& length, AnimGraphNode* node)
|
|||||||
// TODO: lock graph or graph asset here? make it thread safe
|
// TODO: lock graph or graph asset here? make it thread safe
|
||||||
|
|
||||||
length = 0.0f;
|
length = 0.0f;
|
||||||
for (int32 i = 0; i < ARRAY_COUNT(node->Assets); i++)
|
for (int32 i = 0; i < node->Assets.Count(); i++)
|
||||||
{
|
{
|
||||||
if (node->Assets[i])
|
auto& asset = node->Assets[i];
|
||||||
|
if (asset)
|
||||||
{
|
{
|
||||||
// TODO: maybe don't update if not all anims are loaded? just skip the node with the bind pose?
|
// TODO: maybe don't update if not all anims are loaded? just skip the node with the bind pose?
|
||||||
if (node->Assets[i]->WaitForLoaded())
|
if (asset->WaitForLoaded())
|
||||||
{
|
{
|
||||||
node->Assets[i] = nullptr;
|
asset = nullptr;
|
||||||
LOG(Warning, "Failed to load one of the animations.");
|
LOG(Warning, "Failed to load one of the animations.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto anim = node->Assets[i].As<Animation>();
|
const auto anim = asset.As<Animation>();
|
||||||
const auto aData = node->Values[4 + i * 2].AsFloat4();
|
const auto aData = node->Values[4 + i * 2].AsFloat4();
|
||||||
length = Math::Max(length, anim->GetLength() * Math::Abs(aData.W));
|
length = Math::Max(length, anim->GetLength() * Math::Abs(aData.W));
|
||||||
}
|
}
|
||||||
@@ -1268,13 +1270,20 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
auto& data = node->Data.MultiBlend1D;
|
auto& data = node->Data.MultiBlend1D;
|
||||||
|
|
||||||
// Check if not valid animation binded
|
// Check if not valid animation binded
|
||||||
if (data.IndicesSorted[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
|
if (data.Count == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Get axis X
|
// Get axis X
|
||||||
float x = (float)tryGetValue(node->GetBox(4), Value::Zero);
|
float x = (float)tryGetValue(node->GetBox(4), Value::Zero);
|
||||||
x = Math::Clamp(x, range.X, range.Y);
|
x = Math::Clamp(x, range.X, range.Y);
|
||||||
|
|
||||||
|
// Add to trace
|
||||||
|
if (context.Data->EnableTracing)
|
||||||
|
{
|
||||||
|
auto& trace = context.AddTraceEvent(node);
|
||||||
|
trace.Value = x;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if need to evaluate multi blend length
|
// Check if need to evaluate multi blend length
|
||||||
if (data.Length < 0)
|
if (data.Length < 0)
|
||||||
ComputeMultiBlendLength(data.Length, node);
|
ComputeMultiBlendLength(data.Length, node);
|
||||||
@@ -1292,7 +1301,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
ANIM_GRAPH_PROFILE_EVENT("Multi Blend 1D");
|
ANIM_GRAPH_PROFILE_EVENT("Multi Blend 1D");
|
||||||
|
|
||||||
// Find 2 animations to blend (line)
|
// Find 2 animations to blend (line)
|
||||||
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS - 1; i++)
|
for (int32 i = 0; i < data.Count - 1; i++)
|
||||||
{
|
{
|
||||||
const auto a = data.IndicesSorted[i];
|
const auto a = data.IndicesSorted[i];
|
||||||
const auto b = data.IndicesSorted[i + 1];
|
const auto b = data.IndicesSorted[i + 1];
|
||||||
@@ -1302,14 +1311,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
auto aData = node->Values[4 + a * 2].AsFloat4();
|
auto aData = node->Values[4 + a * 2].AsFloat4();
|
||||||
|
|
||||||
// Check single A case or the last valid animation
|
// Check single A case or the last valid animation
|
||||||
if (x <= aData.X + ANIM_GRAPH_BLEND_THRESHOLD || b == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
|
if (x <= aData.X + ANIM_GRAPH_BLEND_THRESHOLD || b == ANIM_GRAPH_MULTI_BLEND_INVALID)
|
||||||
{
|
{
|
||||||
value = SampleAnimation(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, aData.W);
|
value = SampleAnimation(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, aData.W);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get B animation data
|
// Get B animation data
|
||||||
ASSERT(b != ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS);
|
ASSERT(b != ANIM_GRAPH_MULTI_BLEND_INVALID);
|
||||||
const auto bAnim = node->Assets[b].As<Animation>();
|
const auto bAnim = node->Assets[b].As<Animation>();
|
||||||
auto bData = node->Values[4 + b * 2].AsFloat4();
|
auto bData = node->Values[4 + b * 2].AsFloat4();
|
||||||
|
|
||||||
@@ -1357,7 +1366,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
auto& data = node->Data.MultiBlend2D;
|
auto& data = node->Data.MultiBlend2D;
|
||||||
|
|
||||||
// Check if not valid animation binded
|
// Check if not valid animation binded
|
||||||
if (data.TrianglesP0[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
|
if (data.TrianglesCount == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Get axis X
|
// Get axis X
|
||||||
@@ -1368,6 +1377,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
float y = (float)tryGetValue(node->GetBox(5), Value::Zero);
|
float y = (float)tryGetValue(node->GetBox(5), Value::Zero);
|
||||||
y = Math::Clamp(y, range.Z, range.W);
|
y = Math::Clamp(y, range.Z, range.W);
|
||||||
|
|
||||||
|
// Add to trace
|
||||||
|
if (context.Data->EnableTracing)
|
||||||
|
{
|
||||||
|
auto& trace = context.AddTraceEvent(node);
|
||||||
|
const Half2 packed(x, y); // Pack xy into 32-bits
|
||||||
|
*(uint32*)&trace.Value = *(uint32*)&packed;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if need to evaluate multi blend length
|
// Check if need to evaluate multi blend length
|
||||||
if (data.Length < 0)
|
if (data.Length < 0)
|
||||||
ComputeMultiBlendLength(data.Length, node);
|
ComputeMultiBlendLength(data.Length, node);
|
||||||
@@ -1390,20 +1407,20 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
Float2 bestPoint;
|
Float2 bestPoint;
|
||||||
float bestWeight = 0.0f;
|
float bestWeight = 0.0f;
|
||||||
byte bestAnims[2];
|
byte bestAnims[2];
|
||||||
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS && data.TrianglesP0[i] != ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
|
for (int32 i = 0, t = 0; i < data.TrianglesCount; i++)
|
||||||
{
|
{
|
||||||
// Get A animation data
|
// Get A animation data
|
||||||
const auto a = data.TrianglesP0[i];
|
const auto a = data.Triangles[t++];
|
||||||
const auto aAnim = node->Assets[a].As<Animation>();
|
const auto aAnim = node->Assets[a].As<Animation>();
|
||||||
const auto aData = node->Values[4 + a * 2].AsFloat4();
|
const auto aData = node->Values[4 + a * 2].AsFloat4();
|
||||||
|
|
||||||
// Get B animation data
|
// Get B animation data
|
||||||
const auto b = data.TrianglesP1[i];
|
const auto b = data.Triangles[t++];
|
||||||
const auto bAnim = node->Assets[b].As<Animation>();
|
const auto bAnim = node->Assets[b].As<Animation>();
|
||||||
const auto bData = node->Values[4 + b * 2].AsFloat4();
|
const auto bData = node->Values[4 + b * 2].AsFloat4();
|
||||||
|
|
||||||
// Get C animation data
|
// Get C animation data
|
||||||
const auto c = data.TrianglesP2[i];
|
const auto c = data.Triangles[t++];
|
||||||
const auto cAnim = node->Assets[c].As<Animation>();
|
const auto cAnim = node->Assets[c].As<Animation>();
|
||||||
const auto cData = node->Values[4 + c * 2].AsFloat4();
|
const auto cData = node->Values[4 + c * 2].AsFloat4();
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ void InverseKinematics::SolveAimIK(const Transform& node, const Vector3& target,
|
|||||||
Quaternion::FindBetween(fromNode, toTarget, outNodeCorrection);
|
Quaternion::FindBetween(fromNode, toTarget, outNodeCorrection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJointTransform, Transform& endEffectorTransform, const Vector3& targetPosition, const Vector3& poleVector, bool allowStretching, float maxStretchScale)
|
void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJointTransform, Transform& endEffectorTransform, const Vector3& targetPosition, const Vector3& poleVector, bool allowStretching, float maxStretchScale)
|
||||||
{
|
{
|
||||||
// Calculate limb segment lengths
|
// Calculate limb segment lengths
|
||||||
@@ -82,17 +81,20 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
|||||||
Vector3 newEndEffectorPos = targetPosition;
|
Vector3 newEndEffectorPos = targetPosition;
|
||||||
Vector3 newMidJointPos = midJointPos;
|
Vector3 newMidJointPos = midJointPos;
|
||||||
|
|
||||||
if (toTargetLength >= totalLimbLength) {
|
if (toTargetLength >= totalLimbLength)
|
||||||
|
{
|
||||||
// Target is beyond the reach of the limb
|
// Target is beyond the reach of the limb
|
||||||
Vector3 rootToEnd = (targetPosition - rootTransform.Translation).GetNormalized();
|
Vector3 rootToEnd = (targetPosition - rootTransform.Translation).GetNormalized();
|
||||||
|
|
||||||
// Calculate the slight offset towards the pole vector
|
// Calculate the slight offset towards the pole vector
|
||||||
Vector3 rootToPole = (poleVector - rootTransform.Translation).GetNormalized();
|
Vector3 rootToPole = (poleVector - rootTransform.Translation).GetNormalized();
|
||||||
Vector3 slightBendDirection = Vector3::Cross(rootToEnd, rootToPole);
|
Vector3 slightBendDirection = Vector3::Cross(rootToEnd, rootToPole);
|
||||||
if (slightBendDirection.LengthSquared() < ZeroTolerance * ZeroTolerance) {
|
if (slightBendDirection.LengthSquared() < ZeroTolerance * ZeroTolerance)
|
||||||
|
{
|
||||||
slightBendDirection = Vector3::Up;
|
slightBendDirection = Vector3::Up;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
slightBendDirection.Normalize();
|
slightBendDirection.Normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +142,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
|||||||
rootTransform.Orientation = newRootJointOrientation;
|
rootTransform.Orientation = newRootJointOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update mid joint orientation to point Y-axis towards the end effector and Z-axis perpendicular to the IK plane
|
// Update mid joint orientation to point Y-axis towards the end effector and Z-axis perpendicular to the IK plane
|
||||||
{
|
{
|
||||||
// Vector from mid joint to end effector (local Y-axis direction after rotation)
|
// Vector from mid joint to end effector (local Y-axis direction after rotation)
|
||||||
@@ -150,7 +151,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
|||||||
Vector3 rootToMid = (newMidJointPos - rootTransform.Translation).GetNormalized();
|
Vector3 rootToMid = (newMidJointPos - rootTransform.Translation).GetNormalized();
|
||||||
Vector3 planeNormal = Vector3::Cross(rootToMid, midToEnd).GetNormalized();
|
Vector3 planeNormal = Vector3::Cross(rootToMid, midToEnd).GetNormalized();
|
||||||
|
|
||||||
|
|
||||||
// Vector from mid joint to end effector (local Y-axis direction)
|
// Vector from mid joint to end effector (local Y-axis direction)
|
||||||
Vector3 localY = (newEndEffectorPos - newMidJointPos).GetNormalized();
|
Vector3 localY = (newEndEffectorPos - newMidJointPos).GetNormalized();
|
||||||
|
|
||||||
@@ -163,7 +163,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
|
|||||||
// Correct the local Z-axis direction based on the cross product of X and Y to ensure orthogonality
|
// Correct the local Z-axis direction based on the cross product of X and Y to ensure orthogonality
|
||||||
localZ = Vector3::Cross(localX, localY).GetNormalized();
|
localZ = Vector3::Cross(localX, localY).GetNormalized();
|
||||||
|
|
||||||
|
|
||||||
// Construct a rotation from the orthogonal basis vectors
|
// Construct a rotation from the orthogonal basis vectors
|
||||||
// The axes are used differently here than a standard LookRotation to align Z towards the end and Y perpendicular
|
// The axes are used differently here than a standard LookRotation to align Z towards the end and Y perpendicular
|
||||||
Quaternion newMidJointOrientation = Quaternion::LookRotation(localZ, localY); // Assuming FromLookRotation creates a rotation with the first vector as forward and the second as up
|
Quaternion newMidJointOrientation = Quaternion::LookRotation(localZ, localY); // Assuming FromLookRotation creates a rotation with the first vector as forward and the second as up
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const BytesContainer& SceneAnimation::LoadTimeline()
|
|||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
|
|
||||||
bool SceneAnimation::SaveTimeline(BytesContainer& data)
|
bool SceneAnimation::SaveTimeline(const BytesContainer& data)
|
||||||
{
|
{
|
||||||
// Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error)
|
// Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error)
|
||||||
if (LastLoadFailed())
|
if (LastLoadFailed())
|
||||||
|
|||||||
@@ -454,10 +454,10 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the serialized timeline data to the asset.
|
/// Saves the serialized timeline data to the asset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>The cannot be used by virtual assets.</remarks>
|
/// <remarks>It cannot be used by virtual assets.</remarks>
|
||||||
/// <param name="data">The timeline data container.</param>
|
/// <param name="data">The timeline data container.</param>
|
||||||
/// <returns><c>true</c> failed to save data; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> failed to save data; otherwise, <c>false</c>.</returns>
|
||||||
API_FUNCTION() bool SaveTimeline(BytesContainer& data);
|
API_FUNCTION() bool SaveTimeline(const BytesContainer& data);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -587,8 +587,8 @@ bool Asset::IsInternalType() const
|
|||||||
|
|
||||||
bool Asset::onLoad(LoadAssetTask* task)
|
bool Asset::onLoad(LoadAssetTask* task)
|
||||||
{
|
{
|
||||||
// It may fail when task is cancelled and new one is created later (don't crash but just end with an error)
|
|
||||||
if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0)
|
if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0)
|
||||||
|
// It may fail when task is cancelled and new one was created later (don't crash but just end with an error)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Locker.Lock();
|
Locker.Lock();
|
||||||
@@ -601,7 +601,6 @@ bool Asset::onLoad(LoadAssetTask* task)
|
|||||||
}
|
}
|
||||||
const bool isLoaded = result == LoadResult::Ok;
|
const bool isLoaded = result == LoadResult::Ok;
|
||||||
const bool failed = !isLoaded;
|
const bool failed = !isLoaded;
|
||||||
LoadState state = LoadState::Loaded;
|
|
||||||
Platform::AtomicStore(&_loadState, (int64)(isLoaded ? LoadState::Loaded : LoadState::LoadFailed));
|
Platform::AtomicStore(&_loadState, (int64)(isLoaded ? LoadState::Loaded : LoadState::LoadFailed));
|
||||||
if (failed)
|
if (failed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path to the asset storage file. In Editor it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers.
|
/// Gets the path to the asset storage file. In Editor, it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_PROPERTY() virtual const String& GetPath() const = 0;
|
API_PROPERTY() virtual const String& GetPath() const = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -550,7 +550,7 @@ BytesContainer Material::LoadSurface(bool createDefaultIfMissing)
|
|||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
|
|
||||||
bool Material::SaveSurface(BytesContainer& data, const MaterialInfo& info)
|
bool Material::SaveSurface(const BytesContainer& data, const MaterialInfo& info)
|
||||||
{
|
{
|
||||||
// Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error)
|
// Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error)
|
||||||
if (LastLoadFailed())
|
if (LastLoadFailed())
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public:
|
|||||||
/// <param name="data">The surface graph data.</param>
|
/// <param name="data">The surface graph data.</param>
|
||||||
/// <param name="info">The material info structure.</param>
|
/// <param name="info">The material info structure.</param>
|
||||||
/// <returns>True if cannot save it, otherwise false.</returns>
|
/// <returns>True if cannot save it, otherwise false.</returns>
|
||||||
API_FUNCTION() bool SaveSurface(BytesContainer& data, const MaterialInfo& info);
|
API_FUNCTION() bool SaveSurface(const BytesContainer& data, const MaterialInfo& info);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -507,8 +507,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="asset">The asset.</param>
|
/// <param name="asset">The asset.</param>
|
||||||
InitAssetTask(BinaryAsset* asset)
|
InitAssetTask(BinaryAsset* asset)
|
||||||
: ContentLoadTask(Type::Custom)
|
: _asset(asset)
|
||||||
, _asset(asset)
|
|
||||||
, _dataLock(asset->Storage->Lock())
|
, _dataLock(asset->Storage->Lock())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -527,8 +526,6 @@ protected:
|
|||||||
AssetReference<BinaryAsset> ref = _asset.Get();
|
AssetReference<BinaryAsset> ref = _asset.Get();
|
||||||
if (ref == nullptr)
|
if (ref == nullptr)
|
||||||
return Result::MissingReferences;
|
return Result::MissingReferences;
|
||||||
|
|
||||||
// Prepare
|
|
||||||
auto storage = ref->Storage;
|
auto storage = ref->Storage;
|
||||||
auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName());
|
auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName());
|
||||||
ASSERT(factory);
|
ASSERT(factory);
|
||||||
@@ -548,7 +545,6 @@ protected:
|
|||||||
_dataLock.Release();
|
_dataLock.Release();
|
||||||
_asset = nullptr;
|
_asset = nullptr;
|
||||||
|
|
||||||
// Base
|
|
||||||
ContentLoadTask::OnEnd();
|
ContentLoadTask::OnEnd();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -403,7 +403,7 @@ ContentStats Content::GetStats()
|
|||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset* Content::LoadAsyncInternal(const StringView& internalPath, MClass* type)
|
Asset* Content::LoadAsyncInternal(const StringView& internalPath, const MClass* type)
|
||||||
{
|
{
|
||||||
CHECK_RETURN(type, nullptr);
|
CHECK_RETURN(type, nullptr);
|
||||||
const auto scriptingType = Scripting::FindScriptingType(type->GetFullName());
|
const auto scriptingType = Scripting::FindScriptingType(type->GetFullName());
|
||||||
@@ -445,7 +445,7 @@ FLAXENGINE_API Asset* LoadAsset(const Guid& id, const ScriptingTypeHandle& type)
|
|||||||
return Content::LoadAsync(id, type);
|
return Content::LoadAsync(id, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset* Content::LoadAsync(const StringView& path, MClass* type)
|
Asset* Content::LoadAsync(const StringView& path, const MClass* type)
|
||||||
{
|
{
|
||||||
CHECK_RETURN(type, nullptr);
|
CHECK_RETURN(type, nullptr);
|
||||||
const auto scriptingType = Scripting::FindScriptingType(type->GetFullName());
|
const auto scriptingType = Scripting::FindScriptingType(type->GetFullName());
|
||||||
@@ -832,7 +832,7 @@ void Content::UnloadAsset(Asset* asset)
|
|||||||
asset->DeleteObject();
|
asset->DeleteObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset* Content::CreateVirtualAsset(MClass* type)
|
Asset* Content::CreateVirtualAsset(const MClass* type)
|
||||||
{
|
{
|
||||||
CHECK_RETURN(type, nullptr);
|
CHECK_RETURN(type, nullptr);
|
||||||
const auto scriptingType = Scripting::FindScriptingType(type->GetFullName());
|
const auto scriptingType = Scripting::FindScriptingType(type->GetFullName());
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ public:
|
|||||||
/// <param name="path">The path of the asset (absolute or relative to the current workspace directory).</param>
|
/// <param name="path">The path of the asset (absolute or relative to the current workspace directory).</param>
|
||||||
/// <param name="type">The asset type. If loaded object has different type (excluding types derived from the given) the loading fails.</param>
|
/// <param name="type">The asset type. If loaded object has different type (excluding types derived from the given) the loading fails.</param>
|
||||||
/// <returns>Loaded asset or null if cannot</returns>
|
/// <returns>Loaded asset or null if cannot</returns>
|
||||||
API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsync(const StringView& path, MClass* type);
|
API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsync(const StringView& path, const MClass* type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
|
/// Loads asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
|
||||||
@@ -192,7 +192,7 @@ public:
|
|||||||
/// <param name="internalPath">The path of the asset relative to the engine internal content (excluding the extension).</param>
|
/// <param name="internalPath">The path of the asset relative to the engine internal content (excluding the extension).</param>
|
||||||
/// <param name="type">The asset type. If loaded object has different type (excluding types derived from the given) the loading fails.</param>
|
/// <param name="type">The asset type. If loaded object has different type (excluding types derived from the given) the loading fails.</param>
|
||||||
/// <returns>The loaded asset or null if failed.</returns>
|
/// <returns>The loaded asset or null if failed.</returns>
|
||||||
API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsyncInternal(const StringView& internalPath, MClass* type);
|
API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsyncInternal(const StringView& internalPath, const MClass* type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads internal engine asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
|
/// Loads internal engine asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
|
||||||
@@ -342,7 +342,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The asset type klass.</param>
|
/// <param name="type">The asset type klass.</param>
|
||||||
/// <returns>Created asset or null if failed.</returns>
|
/// <returns>Created asset or null if failed.</returns>
|
||||||
API_FUNCTION() static Asset* CreateVirtualAsset(API_PARAM(Attributes="TypeReference(typeof(Asset))") MClass* type);
|
API_FUNCTION() static Asset* CreateVirtualAsset(API_PARAM(Attributes="TypeReference(typeof(Asset))") const MClass* type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates temporary and virtual asset of the given type.
|
/// Creates temporary and virtual asset of the given type.
|
||||||
|
|||||||
@@ -15,52 +15,11 @@ class ContentLoadTask : public Task
|
|||||||
friend LoadingThread;
|
friend LoadingThread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
|
||||||
/// Describes work type
|
|
||||||
/// </summary>
|
|
||||||
DECLARE_ENUM_3(Type, Custom, LoadAsset, LoadAssetData);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Describes work result value
|
/// Describes work result value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
DECLARE_ENUM_5(Result, Ok, AssetLoadError, MissingReferences, LoadDataError, TaskFailed);
|
DECLARE_ENUM_5(Result, Ok, AssetLoadError, MissingReferences, LoadDataError, TaskFailed);
|
||||||
|
|
||||||
private:
|
|
||||||
/// <summary>
|
|
||||||
/// Task type
|
|
||||||
/// </summary>
|
|
||||||
Type _type;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ContentLoadTask"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The task type.</param>
|
|
||||||
ContentLoadTask(const Type type)
|
|
||||||
: _type(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a task type.
|
|
||||||
/// </summary>
|
|
||||||
FORCE_INLINE Type GetType() const
|
|
||||||
{
|
|
||||||
return _type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if async task is loading given asset resource
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="asset">Target asset to check</param>
|
|
||||||
/// <returns>True if is loading that asset, otherwise false</returns>
|
|
||||||
bool IsLoading(Asset* asset) const
|
|
||||||
{
|
|
||||||
return _type == Type::LoadAsset && HasReference((Object*)asset);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual Result run() = 0;
|
virtual Result run() = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "ContentLoadTask.h"
|
#include "ContentLoadTask.h"
|
||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
#include "Engine/Core/Math/Math.h"
|
#include "Engine/Core/Math/Math.h"
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
#include "Engine/Platform/CPUInfo.h"
|
#include "Engine/Platform/CPUInfo.h"
|
||||||
#include "Engine/Platform/Thread.h"
|
#include "Engine/Platform/Thread.h"
|
||||||
#include "Engine/Platform/ConditionVariable.h"
|
#include "Engine/Platform/ConditionVariable.h"
|
||||||
@@ -212,7 +213,7 @@ void ContentLoadingManagerService::Dispose()
|
|||||||
|
|
||||||
String ContentLoadTask::ToString() const
|
String ContentLoadTask::ToString() const
|
||||||
{
|
{
|
||||||
return String::Format(TEXT("Content Load Task {0} ({1})"), ToString(GetType()), (int32)GetState());
|
return String::Format(TEXT("Content Load Task ({})"), (int32)GetState());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentLoadTask::Enqueue()
|
void ContentLoadTask::Enqueue()
|
||||||
|
|||||||
@@ -15,28 +15,24 @@
|
|||||||
class LoadAssetDataTask : public ContentLoadTask
|
class LoadAssetDataTask : public ContentLoadTask
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
WeakAssetReference<BinaryAsset> _asset; // Don't keep ref to the asset (so it can be unloaded if none using it, task will fail then)
|
WeakAssetReference<BinaryAsset> _asset; // Don't keep ref to the asset (so it can be unloaded if none using it, task will fail then)
|
||||||
AssetChunksFlag _chunks;
|
AssetChunksFlag _chunks;
|
||||||
FlaxStorage::LockData _dataLock;
|
FlaxStorage::LockData _dataLock;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LoadAssetDataTask"/> class.
|
/// Initializes a new instance of the <see cref="LoadAssetDataTask"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="asset">The asset to load.</param>
|
/// <param name="asset">The asset to load.</param>
|
||||||
/// <param name="chunks">The chunks to load.</param>
|
/// <param name="chunks">The chunks to load.</param>
|
||||||
LoadAssetDataTask(BinaryAsset* asset, AssetChunksFlag chunks)
|
LoadAssetDataTask(BinaryAsset* asset, AssetChunksFlag chunks)
|
||||||
: ContentLoadTask(Type::LoadAssetData)
|
: _asset(asset)
|
||||||
, _asset(asset)
|
|
||||||
, _chunks(chunks)
|
, _chunks(chunks)
|
||||||
, _dataLock(asset->Storage->Lock())
|
, _dataLock(asset->Storage->Lock())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [ContentLoadTask]
|
// [ContentLoadTask]
|
||||||
bool HasReference(Object* obj) const override
|
bool HasReference(Object* obj) const override
|
||||||
{
|
{
|
||||||
@@ -44,7 +40,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// [ContentLoadTask]
|
// [ContentLoadTask]
|
||||||
Result run() override
|
Result run() override
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,38 +15,35 @@
|
|||||||
class LoadAssetTask : public ContentLoadTask
|
class LoadAssetTask : public ContentLoadTask
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LoadAssetTask"/> class.
|
/// Initializes a new instance of the <see cref="LoadAssetTask"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="asset">The asset to load.</param>
|
/// <param name="asset">The asset to load.</param>
|
||||||
LoadAssetTask(Asset* asset)
|
LoadAssetTask(Asset* asset)
|
||||||
: ContentLoadTask(Type::LoadAsset)
|
: Asset(asset)
|
||||||
, Asset(asset)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~LoadAssetTask()
|
~LoadAssetTask()
|
||||||
{
|
{
|
||||||
if (Asset)
|
auto asset = Asset.Get();
|
||||||
|
if (asset)
|
||||||
{
|
{
|
||||||
Asset->Locker.Lock();
|
asset->Locker.Lock();
|
||||||
if (Platform::AtomicRead(&Asset->_loadingTask) == (intptr)this)
|
if (Platform::AtomicRead(&asset->_loadingTask) == (intptr)this)
|
||||||
{
|
{
|
||||||
Platform::AtomicStore(&Asset->_loadState, (int64)Asset::LoadState::LoadFailed);
|
Platform::AtomicStore(&asset->_loadState, (int64)Asset::LoadState::LoadFailed);
|
||||||
Platform::AtomicStore(&Asset->_loadingTask, 0);
|
Platform::AtomicStore(&asset->_loadingTask, 0);
|
||||||
LOG(Error, "Loading asset \'{0}\' result: {1}.", ToString(), ToString(Result::TaskFailed));
|
LOG(Error, "Loading asset \'{0}\' result: {1}.", ToString(), ToString(Result::TaskFailed));
|
||||||
}
|
}
|
||||||
Asset->Locker.Unlock();
|
asset->Locker.Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
WeakAssetReference<Asset> Asset;
|
WeakAssetReference<Asset> Asset;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [ContentLoadTask]
|
// [ContentLoadTask]
|
||||||
bool HasReference(Object* obj) const override
|
bool HasReference(Object* obj) const override
|
||||||
{
|
{
|
||||||
@@ -54,7 +51,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// [ContentLoadTask]
|
// [ContentLoadTask]
|
||||||
Result run() override
|
Result run() override
|
||||||
{
|
{
|
||||||
@@ -68,32 +64,36 @@ protected:
|
|||||||
// Call loading
|
// Call loading
|
||||||
if (ref->onLoad(this))
|
if (ref->onLoad(this))
|
||||||
return Result::AssetLoadError;
|
return Result::AssetLoadError;
|
||||||
|
|
||||||
return Result::Ok;
|
return Result::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnFail() override
|
void OnFail() override
|
||||||
{
|
{
|
||||||
if (Asset)
|
auto asset = Asset.Get();
|
||||||
|
if (asset)
|
||||||
{
|
{
|
||||||
Asset->Locker.Lock();
|
|
||||||
if (Platform::AtomicRead(&Asset->_loadingTask) == (intptr)this)
|
|
||||||
Platform::AtomicStore(&Asset->_loadingTask, 0);
|
|
||||||
Asset->Locker.Unlock();
|
|
||||||
Asset = nullptr;
|
Asset = nullptr;
|
||||||
|
asset->Locker.Lock();
|
||||||
|
if (Platform::AtomicRead(&asset->_loadingTask) == (intptr)this)
|
||||||
|
Platform::AtomicStore(&asset->_loadingTask, 0);
|
||||||
|
asset->Locker.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
ContentLoadTask::OnFail();
|
ContentLoadTask::OnFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnEnd() override
|
void OnEnd() override
|
||||||
{
|
{
|
||||||
if (Asset)
|
auto asset = Asset.Get();
|
||||||
|
if (asset)
|
||||||
{
|
{
|
||||||
Asset->Locker.Lock();
|
|
||||||
if (Platform::AtomicRead(&Asset->_loadingTask) == (intptr)this)
|
|
||||||
Platform::AtomicStore(&Asset->_loadingTask, 0);
|
|
||||||
Asset->Locker.Unlock();
|
|
||||||
Asset = nullptr;
|
Asset = nullptr;
|
||||||
|
asset->Locker.Lock();
|
||||||
|
if (Platform::AtomicRead(&asset->_loadingTask) == (intptr)this)
|
||||||
|
Platform::AtomicStore(&asset->_loadingTask, 0);
|
||||||
|
asset->Locker.Unlock();
|
||||||
|
asset = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ public:
|
|||||||
/// <param name="silentMode">In silent mode don't reload opened storage container that is using target file.</param>
|
/// <param name="silentMode">In silent mode don't reload opened storage container that is using target file.</param>
|
||||||
/// <param name="customData">Custom options.</param>
|
/// <param name="customData">Custom options.</param>
|
||||||
/// <returns>True if cannot create package, otherwise false</returns>
|
/// <returns>True if cannot create package, otherwise false</returns>
|
||||||
FORCE_INLINE static bool Create(const StringView& path, AssetInitData& data, bool silentMode = false, const CustomData* customData = nullptr)
|
FORCE_INLINE static bool Create(const StringView& path, const AssetInitData& data, bool silentMode = false, const CustomData* customData = nullptr)
|
||||||
{
|
{
|
||||||
return Create(path, &data, 1, silentMode, customData);
|
return Create(path, &data, 1, silentMode, customData);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -467,7 +467,7 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context)
|
|||||||
writer.StartObject();
|
writer.StartObject();
|
||||||
eKey.Value.Instance->Serialize(writer, nullptr);
|
eKey.Value.Instance->Serialize(writer, nullptr);
|
||||||
writer.EndObject();
|
writer.EndObject();
|
||||||
cloneKey.Value.JsonData.Set(buffer.GetString(), buffer.GetSize());
|
cloneKey.Value.JsonData.Set(buffer.GetString(), (int32)buffer.GetSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1281,8 +1281,8 @@ namespace FlaxEngine
|
|||||||
/// <returns>The position snapped to the grid.</returns>
|
/// <returns>The position snapped to the grid.</returns>
|
||||||
public static Float2 SnapToGrid(Float2 pos, Float2 gridSize)
|
public static Float2 SnapToGrid(Float2 pos, Float2 gridSize)
|
||||||
{
|
{
|
||||||
pos.X = Mathf.Ceil((pos.X - (gridSize.X * 0.5f)) / gridSize.Y) * gridSize.X;
|
pos.X = Mathf.Ceil((pos.X - (gridSize.X * 0.5f)) / gridSize.X) * gridSize.X;
|
||||||
pos.Y = Mathf.Ceil((pos.Y - (gridSize.Y * 0.5f)) / gridSize.X) * gridSize.Y;
|
pos.Y = Mathf.Ceil((pos.Y - (gridSize.Y * 0.5f)) / gridSize.Y) * gridSize.Y;
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -774,7 +774,7 @@ void Matrix::Transformation(const Float3& scalingCenter, const Quaternion& scali
|
|||||||
result = Translation(-scalingCenter) * Transpose(sr) * Scaling(scaling) * sr * Translation(scalingCenter) * Translation(-rotationCenter) * RotationQuaternion(rotation) * Translation(rotationCenter) * Translation(translation);
|
result = Translation(-scalingCenter) * Transpose(sr) * Scaling(scaling) * sr * Translation(scalingCenter) * Translation(-rotationCenter) * RotationQuaternion(rotation) * Translation(rotationCenter) * Translation(translation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Matrix::Transformation2D(Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result)
|
void Matrix::Transformation2D(const Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result)
|
||||||
{
|
{
|
||||||
result = Translation((Float3)-scalingCenter) * RotationZ(-scalingRotation) * Scaling((Float3)scaling) * RotationZ(scalingRotation) * Translation((Float3)scalingCenter) * Translation((Float3)-rotationCenter) * RotationZ(rotation) * Translation((Float3)rotationCenter) * Translation((Float3)translation);
|
result = Translation((Float3)-scalingCenter) * RotationZ(-scalingRotation) * Scaling((Float3)scaling) * RotationZ(scalingRotation) * Translation((Float3)scalingCenter) * Translation((Float3)-rotationCenter) * RotationZ(rotation) * Translation((Float3)rotationCenter) * Translation((Float3)translation);
|
||||||
result.M33 = 1.0f;
|
result.M33 = 1.0f;
|
||||||
|
|||||||
@@ -941,7 +941,7 @@ public:
|
|||||||
// @param rotation The rotation of the transformation.
|
// @param rotation The rotation of the transformation.
|
||||||
// @param translation The translation factor of the transformation.
|
// @param translation The translation factor of the transformation.
|
||||||
// @param result When the method completes, contains the created transformation matrix.
|
// @param result When the method completes, contains the created transformation matrix.
|
||||||
static void Transformation2D(Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result);
|
static void Transformation2D(const Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result);
|
||||||
|
|
||||||
// Creates a world matrix with the specified parameters.
|
// Creates a world matrix with the specified parameters.
|
||||||
// @param position Position of the object. This value is used in translation operations.
|
// @param position Position of the object. This value is used in translation operations.
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ Rectangle Rectangle::FromPoints(const Float2& p1, const Float2& p2)
|
|||||||
return Rectangle(upperLeft, Math::Max(rightBottom - upperLeft, Float2::Zero));
|
return Rectangle(upperLeft, Math::Max(rightBottom - upperLeft, Float2::Zero));
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle Rectangle::FromPoints(Float2* points, int32 pointsCount)
|
Rectangle Rectangle::FromPoints(const Float2* points, int32 pointsCount)
|
||||||
{
|
{
|
||||||
ASSERT(pointsCount > 0);
|
ASSERT(pointsCount > 0);
|
||||||
Float2 upperLeft = points[0];
|
Float2 upperLeft = points[0];
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ public:
|
|||||||
// @returns Rectangle that contains both p1 and p2
|
// @returns Rectangle that contains both p1 and p2
|
||||||
static Rectangle FromPoints(const Float2& p1, const Float2& p2);
|
static Rectangle FromPoints(const Float2& p1, const Float2& p2);
|
||||||
|
|
||||||
static Rectangle FromPoints(Float2* points, int32 pointsCount);
|
static Rectangle FromPoints(const Float2* points, int32 pointsCount);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
/// <param name="length">The length.</param>
|
/// <param name="length">The length.</param>
|
||||||
void Append(T* data, int32 length)
|
void Append(const T* data, int32 length)
|
||||||
{
|
{
|
||||||
if (length <= 0)
|
if (length <= 0)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ VariantType::VariantType(Types type, const StringAnsiView& typeName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantType::VariantType(Types type, MClass* klass)
|
VariantType::VariantType(Types type, const MClass* klass)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
TypeName = nullptr;
|
TypeName = nullptr;
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public:
|
|||||||
|
|
||||||
explicit VariantType(Types type, const StringView& typeName);
|
explicit VariantType(Types type, const StringView& typeName);
|
||||||
explicit VariantType(Types type, const StringAnsiView& typeName);
|
explicit VariantType(Types type, const StringAnsiView& typeName);
|
||||||
explicit VariantType(Types type, MClass* klass);
|
explicit VariantType(Types type, const MClass* klass);
|
||||||
explicit VariantType(const StringAnsiView& typeName);
|
explicit VariantType(const StringAnsiView& typeName);
|
||||||
VariantType(const VariantType& other);
|
VariantType(const VariantType& other);
|
||||||
VariantType(VariantType&& other) noexcept;
|
VariantType(VariantType&& other) noexcept;
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ namespace EngineImpl
|
|||||||
|
|
||||||
DateTime Engine::StartupTime;
|
DateTime Engine::StartupTime;
|
||||||
bool Engine::HasFocus = false;
|
bool Engine::HasFocus = false;
|
||||||
|
uint64 Engine::UpdateCount = 0;
|
||||||
uint64 Engine::FrameCount = 0;
|
uint64 Engine::FrameCount = 0;
|
||||||
Action Engine::FixedUpdate;
|
Action Engine::FixedUpdate;
|
||||||
Action Engine::Update;
|
Action Engine::Update;
|
||||||
@@ -156,7 +157,7 @@ int32 Engine::Main(const Char* cmdLine)
|
|||||||
#endif
|
#endif
|
||||||
Log::Logger::WriteFloor();
|
Log::Logger::WriteFloor();
|
||||||
LOG_FLUSH();
|
LOG_FLUSH();
|
||||||
Time::OnBeforeRun();
|
Time::Synchronize();
|
||||||
EngineImpl::IsReady = true;
|
EngineImpl::IsReady = true;
|
||||||
|
|
||||||
// Main engine loop
|
// Main engine loop
|
||||||
@@ -191,8 +192,11 @@ int32 Engine::Main(const Char* cmdLine)
|
|||||||
OnUnpause();
|
OnUnpause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the same time for all ticks to improve synchronization
|
||||||
|
const double time = Platform::GetTimeSeconds();
|
||||||
|
|
||||||
// Update game logic
|
// Update game logic
|
||||||
if (Time::OnBeginUpdate())
|
if (Time::OnBeginUpdate(time))
|
||||||
{
|
{
|
||||||
OnUpdate();
|
OnUpdate();
|
||||||
OnLateUpdate();
|
OnLateUpdate();
|
||||||
@@ -200,7 +204,7 @@ int32 Engine::Main(const Char* cmdLine)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start physics simulation
|
// Start physics simulation
|
||||||
if (Time::OnBeginPhysics())
|
if (Time::OnBeginPhysics(time))
|
||||||
{
|
{
|
||||||
OnFixedUpdate();
|
OnFixedUpdate();
|
||||||
OnLateFixedUpdate();
|
OnLateFixedUpdate();
|
||||||
@@ -208,7 +212,7 @@ int32 Engine::Main(const Char* cmdLine)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw frame
|
// Draw frame
|
||||||
if (Time::OnBeginDraw())
|
if (Time::OnBeginDraw(time))
|
||||||
{
|
{
|
||||||
OnDraw();
|
OnDraw();
|
||||||
Time::OnEndDraw();
|
Time::OnEndDraw();
|
||||||
@@ -296,6 +300,8 @@ void Engine::OnUpdate()
|
|||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Update");
|
PROFILE_CPU_NAMED("Update");
|
||||||
|
|
||||||
|
UpdateCount++;
|
||||||
|
|
||||||
// Update application (will gather data and other platform related events)
|
// Update application (will gather data and other platform related events)
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Platform.Tick");
|
PROFILE_CPU_NAMED("Platform.Tick");
|
||||||
@@ -465,7 +471,7 @@ void Engine::OnUnpause()
|
|||||||
LOG(Info, "App unpaused");
|
LOG(Info, "App unpaused");
|
||||||
Unpause();
|
Unpause();
|
||||||
|
|
||||||
Time::OnBeforeRun();
|
Time::Synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::OnExit()
|
void Engine::OnExit()
|
||||||
|
|||||||
@@ -28,7 +28,12 @@ public:
|
|||||||
API_FIELD(ReadOnly) static bool HasFocus;
|
API_FIELD(ReadOnly) static bool HasFocus;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current frame count since the start of the game.
|
/// Gets the current update counter since the start of the game.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(ReadOnly) static uint64 UpdateCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current frame (drawing) count since the start of the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_FIELD(ReadOnly) static uint64 FrameCount;
|
API_FIELD(ReadOnly) static uint64 FrameCount;
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ void TimeSettings::Apply()
|
|||||||
::MaxUpdateDeltaTime = MaxUpdateDeltaTime;
|
::MaxUpdateDeltaTime = MaxUpdateDeltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Time::TickData::OnBeforeRun(float targetFps, double currentTime)
|
void Time::TickData::Synchronize(float targetFps, double currentTime)
|
||||||
{
|
{
|
||||||
Time = UnscaledTime = TimeSpan::Zero();
|
Time = UnscaledTime = TimeSpan::Zero();
|
||||||
DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero();
|
DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero();
|
||||||
@@ -72,10 +72,9 @@ void Time::TickData::OnReset(float targetFps, double currentTime)
|
|||||||
LastEnd = currentTime;
|
LastEnd = currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::TickData::OnTickBegin(float targetFps, float maxDeltaTime)
|
bool Time::TickData::OnTickBegin(double time, float targetFps, float maxDeltaTime)
|
||||||
{
|
{
|
||||||
// Check if can perform a tick
|
// Check if can perform a tick
|
||||||
const double time = Platform::GetTimeSeconds();
|
|
||||||
double deltaTime;
|
double deltaTime;
|
||||||
if (FixedDeltaTimeEnable)
|
if (FixedDeltaTimeEnable)
|
||||||
{
|
{
|
||||||
@@ -126,10 +125,9 @@ void Time::TickData::Advance(double time, double deltaTime)
|
|||||||
TicksCount++;
|
TicksCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime)
|
bool Time::FixedStepTickData::OnTickBegin(double time, float targetFps, float maxDeltaTime)
|
||||||
{
|
{
|
||||||
// Check if can perform a tick
|
// Check if can perform a tick
|
||||||
double time = Platform::GetTimeSeconds();
|
|
||||||
double deltaTime, minDeltaTime;
|
double deltaTime, minDeltaTime;
|
||||||
if (FixedDeltaTimeEnable)
|
if (FixedDeltaTimeEnable)
|
||||||
{
|
{
|
||||||
@@ -240,18 +238,18 @@ void Time::SetFixedDeltaTime(bool enable, float value)
|
|||||||
FixedDeltaTimeValue = value;
|
FixedDeltaTimeValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Time::OnBeforeRun()
|
void Time::Synchronize()
|
||||||
{
|
{
|
||||||
// Initialize tick data (based on a time settings)
|
// Initialize tick data (based on a time settings)
|
||||||
const double time = Platform::GetTimeSeconds();
|
const double time = Platform::GetTimeSeconds();
|
||||||
Update.OnBeforeRun(UpdateFPS, time);
|
Update.Synchronize(UpdateFPS, time);
|
||||||
Physics.OnBeforeRun(PhysicsFPS, time);
|
Physics.Synchronize(PhysicsFPS, time);
|
||||||
Draw.OnBeforeRun(DrawFPS, time);
|
Draw.Synchronize(DrawFPS, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::OnBeginUpdate()
|
bool Time::OnBeginUpdate(double time)
|
||||||
{
|
{
|
||||||
if (Update.OnTickBegin(UpdateFPS, MaxUpdateDeltaTime))
|
if (Update.OnTickBegin(time, UpdateFPS, MaxUpdateDeltaTime))
|
||||||
{
|
{
|
||||||
Current = &Update;
|
Current = &Update;
|
||||||
return true;
|
return true;
|
||||||
@@ -259,9 +257,9 @@ bool Time::OnBeginUpdate()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::OnBeginPhysics()
|
bool Time::OnBeginPhysics(double time)
|
||||||
{
|
{
|
||||||
if (Physics.OnTickBegin(PhysicsFPS, _physicsMaxDeltaTime))
|
if (Physics.OnTickBegin(time, PhysicsFPS, _physicsMaxDeltaTime))
|
||||||
{
|
{
|
||||||
Current = &Physics;
|
Current = &Physics;
|
||||||
return true;
|
return true;
|
||||||
@@ -269,9 +267,9 @@ bool Time::OnBeginPhysics()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::OnBeginDraw()
|
bool Time::OnBeginDraw(double time)
|
||||||
{
|
{
|
||||||
if (Draw.OnTickBegin(DrawFPS, 1.0f))
|
if (Draw.OnTickBegin(time, DrawFPS, 1.0f))
|
||||||
{
|
{
|
||||||
Current = &Draw;
|
Current = &Draw;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_CLASS(Static) class FLAXENGINE_API Time
|
API_CLASS(Static) class FLAXENGINE_API Time
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Time);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Time);
|
||||||
friend class Engine;
|
friend class Engine;
|
||||||
friend class TimeService;
|
friend class TimeService;
|
||||||
friend class PhysicsSettings;
|
friend class PhysicsSettings;
|
||||||
public:
|
|
||||||
|
|
||||||
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Engine subsystem updating data.
|
/// Engine subsystem updating data.
|
||||||
/// Used to invoke game logic updates, physics updates and rendering with possibly different frequencies.
|
/// Used to invoke game logic updates, physics updates and rendering with possibly different frequencies.
|
||||||
@@ -25,7 +25,6 @@ public:
|
|||||||
class FLAXENGINE_API TickData
|
class FLAXENGINE_API TickData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual ~TickData() = default;
|
virtual ~TickData() = default;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -75,14 +74,12 @@ public:
|
|||||||
TimeSpan UnscaledTime;
|
TimeSpan UnscaledTime;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
virtual void Synchronize(float targetFps, double currentTime);
|
||||||
virtual void OnBeforeRun(float targetFps, double currentTime);
|
|
||||||
virtual void OnReset(float targetFps, double currentTime);
|
virtual void OnReset(float targetFps, double currentTime);
|
||||||
virtual bool OnTickBegin(float targetFps, float maxDeltaTime);
|
virtual bool OnTickBegin(double time, float targetFps, float maxDeltaTime);
|
||||||
virtual void OnTickEnd();
|
virtual void OnTickEnd();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void Advance(double time, double deltaTime);
|
void Advance(double time, double deltaTime);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,25 +89,21 @@ public:
|
|||||||
class FixedStepTickData : public TickData
|
class FixedStepTickData : public TickData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The last few ticks delta times. Used to check if can use fixed steps or whenever is running slowly so should use normal stepping.
|
/// The last few ticks delta times. Used to check if can use fixed steps or whenever is running slowly so should use normal stepping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SamplesBuffer<double, 4> Samples;
|
SamplesBuffer<double, 4> Samples;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [TickData]
|
// [TickData]
|
||||||
bool OnTickBegin(float targetFps, float maxDeltaTime) override;
|
bool OnTickBegin(double time, float targetFps, float maxDeltaTime) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static bool _gamePaused;
|
static bool _gamePaused;
|
||||||
static float _physicsMaxDeltaTime;
|
static float _physicsMaxDeltaTime;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time at which the game started (UTC local).
|
/// The time at which the game started (UTC local).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -140,7 +133,6 @@ public:
|
|||||||
API_FIELD() static float TimeScale;
|
API_FIELD() static float TimeScale;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The game logic updating data.
|
/// The game logic updating data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -162,7 +154,6 @@ public:
|
|||||||
static TickData* Current;
|
static TickData* Current;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current tick data (safety so returns Update tick data if no active).
|
/// Gets the current tick data (safety so returns Update tick data if no active).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -225,15 +216,17 @@ public:
|
|||||||
/// <param name="value">The fixed draw/update rate for the time.</param>
|
/// <param name="value">The fixed draw/update rate for the time.</param>
|
||||||
API_FUNCTION() static void SetFixedDeltaTime(bool enable, float value);
|
API_FUNCTION() static void SetFixedDeltaTime(bool enable, float value);
|
||||||
|
|
||||||
private:
|
/// <summary>
|
||||||
|
/// Synchronizes update, fixed update and draw. Resets any pending deltas for fresh ticking in sync.
|
||||||
|
/// </summary>
|
||||||
|
API_FUNCTION() static void Synchronize();
|
||||||
|
|
||||||
|
private:
|
||||||
// Methods used by the Engine class
|
// Methods used by the Engine class
|
||||||
|
|
||||||
static void OnBeforeRun();
|
static bool OnBeginUpdate(double time);
|
||||||
|
static bool OnBeginPhysics(double time);
|
||||||
static bool OnBeginUpdate();
|
static bool OnBeginDraw(double time);
|
||||||
static bool OnBeginPhysics();
|
|
||||||
static bool OnBeginDraw();
|
|
||||||
|
|
||||||
static void OnEndUpdate();
|
static void OnEndUpdate();
|
||||||
static void OnEndPhysics();
|
static void OnEndPhysics();
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan
|
|||||||
e = &result[key];
|
e = &result[key];
|
||||||
ASSERT_LOW_LAYER(key.Mat);
|
ASSERT_LOW_LAYER(key.Mat);
|
||||||
e->DrawCall.Material = key.Mat;
|
e->DrawCall.Material = key.Mat;
|
||||||
e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr;
|
e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add instance to the draw batch
|
// Add instance to the draw batch
|
||||||
@@ -1172,7 +1172,7 @@ void Foliage::Draw(RenderContext& renderContext)
|
|||||||
draw.ForcedLOD = -1;
|
draw.ForcedLOD = -1;
|
||||||
draw.SortOrder = 0;
|
draw.SortOrder = 0;
|
||||||
draw.VertexColors = nullptr;
|
draw.VertexColors = nullptr;
|
||||||
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex);
|
draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex) : nullptr;
|
||||||
draw.LightmapUVs = &instance.Lightmap.UVsArea;
|
draw.LightmapUVs = &instance.Lightmap.UVsArea;
|
||||||
draw.Buffer = &type.Entries;
|
draw.Buffer = &type.Entries;
|
||||||
draw.World = &world;
|
draw.World = &world;
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ public:
|
|||||||
/// <param name="elementsCount">The elements count.</param>
|
/// <param name="elementsCount">The elements count.</param>
|
||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
/// <returns>The buffer description.</returns>
|
/// <returns>The buffer description.</returns>
|
||||||
static GPUBufferDescription Vertex(int32 elementStride, int32 elementsCount, void* data)
|
static GPUBufferDescription Vertex(int32 elementStride, int32 elementsCount, const void* data)
|
||||||
{
|
{
|
||||||
return Buffer(elementsCount * elementStride, GPUBufferFlags::VertexBuffer, PixelFormat::Unknown, data, elementStride, GPUResourceUsage::Default);
|
return Buffer(elementsCount * elementStride, GPUBufferFlags::VertexBuffer, PixelFormat::Unknown, data, elementStride, GPUResourceUsage::Default);
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@ public:
|
|||||||
/// <param name="elementsCount">The elements count.</param>
|
/// <param name="elementsCount">The elements count.</param>
|
||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
/// <returns>The buffer description.</returns>
|
/// <returns>The buffer description.</returns>
|
||||||
static GPUBufferDescription Index(int32 elementStride, int32 elementsCount, void* data)
|
static GPUBufferDescription Index(int32 elementStride, int32 elementsCount, const void* data)
|
||||||
{
|
{
|
||||||
const auto format = elementStride == 4 ? PixelFormat::R32_UInt : PixelFormat::R16_UInt;
|
const auto format = elementStride == 4 ? PixelFormat::R32_UInt : PixelFormat::R16_UInt;
|
||||||
return Buffer(elementsCount * elementStride, GPUBufferFlags::IndexBuffer, format, data, elementStride, GPUResourceUsage::Default);
|
return Buffer(elementsCount * elementStride, GPUBufferFlags::IndexBuffer, format, data, elementStride, GPUResourceUsage::Default);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current materials shader version.
|
/// Current materials shader version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
#define MATERIAL_GRAPH_VERSION 164
|
#define MATERIAL_GRAPH_VERSION 165
|
||||||
|
|
||||||
class Material;
|
class Material;
|
||||||
class GPUShader;
|
class GPUShader;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
template<typename IndexType>
|
template<typename IndexType>
|
||||||
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, Float3* vertices, IndexType* triangles, Float3* normals, Float3* tangents, Float2* uvs, Color32* colors)
|
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const IndexType* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
|
||||||
{
|
{
|
||||||
auto model = mesh->GetModel();
|
auto model = mesh->GetModel();
|
||||||
CHECK_RETURN(model && model->IsVirtual(), true);
|
CHECK_RETURN(model && model->IsVirtual(), true);
|
||||||
@@ -63,40 +63,39 @@ namespace
|
|||||||
const auto t = Float1010102(Float3::UnitX);
|
const auto t = Float1010102(Float3::UnitX);
|
||||||
for (uint32 i = 0; i < vertexCount; i++)
|
for (uint32 i = 0; i < vertexCount; i++)
|
||||||
{
|
{
|
||||||
vb1[i].Normal = n;
|
vb1.Get()[i].Normal = n;
|
||||||
vb1[i].Tangent = t;
|
vb1.Get()[i].Tangent = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (uvs)
|
if (uvs)
|
||||||
{
|
{
|
||||||
for (uint32 i = 0; i < vertexCount; i++)
|
for (uint32 i = 0; i < vertexCount; i++)
|
||||||
vb1[i].TexCoord = Half2(uvs[i]);
|
vb1.Get()[i].TexCoord = Half2(uvs[i]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto v = Half2::Zero;
|
auto v = Half2::Zero;
|
||||||
for (uint32 i = 0; i < vertexCount; i++)
|
for (uint32 i = 0; i < vertexCount; i++)
|
||||||
vb1[i].TexCoord = v;
|
vb1.Get()[i].TexCoord = v;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto v = Half2::Zero;
|
auto v = Half2::Zero;
|
||||||
for (uint32 i = 0; i < vertexCount; i++)
|
for (uint32 i = 0; i < vertexCount; i++)
|
||||||
vb1[i].LightmapUVs = v;
|
vb1.Get()[i].LightmapUVs = v;
|
||||||
}
|
}
|
||||||
if (colors)
|
if (colors)
|
||||||
{
|
{
|
||||||
vb2.Resize(vertexCount);
|
vb2.Resize(vertexCount);
|
||||||
for (uint32 i = 0; i < vertexCount; i++)
|
for (uint32 i = 0; i < vertexCount; i++)
|
||||||
vb2[i].Color = colors[i];
|
vb2.Get()[i].Color = colors[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vertices, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, triangles);
|
return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vertices, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, triangles);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
|
|
||||||
template<typename IndexType>
|
template<typename IndexType>
|
||||||
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj)
|
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj)
|
||||||
{
|
{
|
||||||
ASSERT((uint32)MCore::Array::GetLength(verticesObj) >= vertexCount);
|
ASSERT((uint32)MCore::Array::GetLength(verticesObj) >= vertexCount);
|
||||||
ASSERT((uint32)MCore::Array::GetLength(trianglesObj) / 3 >= triangleCount);
|
ASSERT((uint32)MCore::Array::GetLength(trianglesObj) / 3 >= triangleCount);
|
||||||
@@ -110,7 +109,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename IndexType>
|
template<typename IndexType>
|
||||||
bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MArray* trianglesObj)
|
bool UpdateTriangles(Mesh* mesh, int32 triangleCount, const MArray* trianglesObj)
|
||||||
{
|
{
|
||||||
const auto model = mesh->GetModel();
|
const auto model = mesh->GetModel();
|
||||||
ASSERT(model && model->IsVirtual() && trianglesObj);
|
ASSERT(model && model->IsVirtual() && trianglesObj);
|
||||||
@@ -121,7 +120,6 @@ namespace
|
|||||||
|
|
||||||
return mesh->UpdateTriangles(triangleCount, ib);
|
return mesh->UpdateTriangles(triangleCount, ib);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +128,7 @@ bool Mesh::HasVertexColors() const
|
|||||||
return _vertexBuffers[2] != nullptr && _vertexBuffers[2]->IsAllocated();
|
return _vertexBuffers[2] != nullptr && _vertexBuffers[2]->IsAllocated();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices)
|
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const void* ib, bool use16BitIndices)
|
||||||
{
|
{
|
||||||
auto model = (Model*)_model;
|
auto model = (Model*)_model;
|
||||||
|
|
||||||
@@ -145,7 +143,7 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType*
|
|||||||
|
|
||||||
// Calculate mesh bounds
|
// Calculate mesh bounds
|
||||||
BoundingBox bounds;
|
BoundingBox bounds;
|
||||||
BoundingBox::FromPoints((Float3*)vb0, vertexCount, bounds);
|
BoundingBox::FromPoints((const Float3*)vb0, vertexCount, bounds);
|
||||||
SetBounds(bounds);
|
SetBounds(bounds);
|
||||||
|
|
||||||
// Send event (actors using this model can update bounds, etc.)
|
// Send event (actors using this model can update bounds, etc.)
|
||||||
@@ -155,17 +153,17 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType*
|
|||||||
return failed;
|
return failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Float3* vertices, uint16* triangles, Float3* normals, Float3* tangents, Float2* uvs, Color32* colors)
|
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint16* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
|
||||||
{
|
{
|
||||||
return ::UpdateMesh<uint16>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
|
return ::UpdateMesh<uint16>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Float3* vertices, uint32* triangles, Float3* normals, Float3* tangents, Float2* uvs, Color32* colors)
|
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors)
|
||||||
{
|
{
|
||||||
return ::UpdateMesh<uint32>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
|
return ::UpdateMesh<uint32>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices)
|
bool Mesh::UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices)
|
||||||
{
|
{
|
||||||
// Cache data
|
// Cache data
|
||||||
uint32 indicesCount = triangleCount * 3;
|
uint32 indicesCount = triangleCount * 3;
|
||||||
@@ -217,7 +215,7 @@ Mesh::~Mesh()
|
|||||||
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* vb1, void* vb2, void* ib, bool use16BitIndexBuffer)
|
bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer)
|
||||||
{
|
{
|
||||||
// Cache data
|
// Cache data
|
||||||
uint32 indicesCount = triangles * 3;
|
uint32 indicesCount = triangles * 3;
|
||||||
@@ -697,22 +695,22 @@ ScriptingObject* Mesh::GetParentModel()
|
|||||||
|
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
|
|
||||||
bool Mesh::UpdateMeshUInt(int32 vertexCount, int32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj)
|
bool Mesh::UpdateMeshUInt(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj)
|
||||||
{
|
{
|
||||||
return ::UpdateMesh<uint32>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
|
return ::UpdateMesh<uint32>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj)
|
bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj)
|
||||||
{
|
{
|
||||||
return ::UpdateMesh<uint16>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
|
return ::UpdateMesh<uint16>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::UpdateTrianglesUInt(int32 triangleCount, MArray* trianglesObj)
|
bool Mesh::UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj)
|
||||||
{
|
{
|
||||||
return ::UpdateTriangles<uint32>(this, triangleCount, trianglesObj);
|
return ::UpdateTriangles<uint32>(this, triangleCount, trianglesObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::UpdateTrianglesUShort(int32 triangleCount, MArray* trianglesObj)
|
bool Mesh::UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj)
|
||||||
{
|
{
|
||||||
return ::UpdateTriangles<uint16>(this, triangleCount, trianglesObj);
|
return ::UpdateTriangles<uint16>(this, triangleCount, trianglesObj);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public:
|
|||||||
/// <param name="vb2">The third vertex buffer data.</param>
|
/// <param name="vb2">The third vertex buffer data.</param>
|
||||||
/// <param name="ib">The index buffer in clockwise order.</param>
|
/// <param name="ib">The index buffer in clockwise order.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint32* ib)
|
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const uint32* ib)
|
||||||
{
|
{
|
||||||
return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, false);
|
return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, false);
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ public:
|
|||||||
/// <param name="vb2">The third vertex buffer data.</param>
|
/// <param name="vb2">The third vertex buffer data.</param>
|
||||||
/// <param name="ib">The index buffer in clockwise order.</param>
|
/// <param name="ib">The index buffer in clockwise order.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint16* ib)
|
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const uint16* ib)
|
||||||
{
|
{
|
||||||
return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, true);
|
return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, true);
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ public:
|
|||||||
/// <param name="ib">The index buffer in clockwise order.</param>
|
/// <param name="ib">The index buffer in clockwise order.</param>
|
||||||
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
|
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices);
|
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const void* ib, bool use16BitIndices);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
|
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
|
||||||
@@ -161,7 +161,7 @@ public:
|
|||||||
/// <param name="uvs">The texture coordinates (per vertex).</param>
|
/// <param name="uvs">The texture coordinates (per vertex).</param>
|
||||||
/// <param name="colors">The vertex colors (per vertex).</param>
|
/// <param name="colors">The vertex colors (per vertex).</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Float3* vertices, uint16* triangles, Float3* normals = nullptr, Float3* tangents = nullptr, Float2* uvs = nullptr, Color32* colors = nullptr);
|
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint16* triangles, const Float3* normals = nullptr, const Float3* tangents = nullptr, const Float2* uvs = nullptr, const Color32* colors = nullptr);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
|
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
|
||||||
@@ -177,7 +177,7 @@ public:
|
|||||||
/// <param name="uvs">The texture coordinates (per vertex).</param>
|
/// <param name="uvs">The texture coordinates (per vertex).</param>
|
||||||
/// <param name="colors">The vertex colors (per vertex).</param>
|
/// <param name="colors">The vertex colors (per vertex).</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Float3* vertices, uint32* triangles, Float3* normals = nullptr, Float3* tangents = nullptr, Float2* uvs = nullptr, Color32* colors = nullptr);
|
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Float3* normals = nullptr, const Float3* tangents = nullptr, const Float2* uvs = nullptr, const Color32* colors = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -186,7 +186,7 @@ public:
|
|||||||
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
||||||
/// <param name="ib">The index buffer.</param>
|
/// <param name="ib">The index buffer.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, uint32* ib)
|
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint32* ib)
|
||||||
{
|
{
|
||||||
return UpdateTriangles(triangleCount, ib, false);
|
return UpdateTriangles(triangleCount, ib, false);
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ public:
|
|||||||
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
||||||
/// <param name="ib">The index buffer.</param>
|
/// <param name="ib">The index buffer.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, uint16* ib)
|
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint16* ib)
|
||||||
{
|
{
|
||||||
return UpdateTriangles(triangleCount, ib, true);
|
return UpdateTriangles(triangleCount, ib, true);
|
||||||
}
|
}
|
||||||
@@ -209,7 +209,7 @@ public:
|
|||||||
/// <param name="ib">The index buffer.</param>
|
/// <param name="ib">The index buffer.</param>
|
||||||
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
|
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
bool UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices);
|
bool UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -235,7 +235,7 @@ public:
|
|||||||
/// <param name="ib">Index buffer data</param>
|
/// <param name="ib">Index buffer data</param>
|
||||||
/// <param name="use16BitIndexBuffer">True if use 16 bit indices for the index buffer (true: uint16, false: uint32).</param>
|
/// <param name="use16BitIndexBuffer">True if use 16 bit indices for the index buffer (true: uint16, false: uint32).</param>
|
||||||
/// <returns>True if cannot load data, otherwise false.</returns>
|
/// <returns>True if cannot load data, otherwise false.</returns>
|
||||||
bool Load(uint32 vertices, uint32 triangles, void* vb0, void* vb1, void* vb2, void* ib, bool use16BitIndexBuffer);
|
bool Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unloads the mesh data (vertex buffers and cache). The opposite to Load.
|
/// Unloads the mesh data (vertex buffers and cache). The opposite to Load.
|
||||||
@@ -315,10 +315,10 @@ private:
|
|||||||
// Internal bindings
|
// Internal bindings
|
||||||
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
|
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
API_FUNCTION(NoProxy) bool UpdateMeshUInt(int32 vertexCount, int32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj);
|
API_FUNCTION(NoProxy) bool UpdateMeshUInt(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj);
|
||||||
API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj);
|
API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj);
|
||||||
API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, MArray* trianglesObj);
|
API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj);
|
||||||
API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, MArray* trianglesObj);
|
API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj);
|
||||||
API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI);
|
API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI);
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -508,6 +508,31 @@ void MeshData::TransformBuffer(const Matrix& matrix)
|
|||||||
Matrix::Invert(matrix, inverseTransposeMatrix);
|
Matrix::Invert(matrix, inverseTransposeMatrix);
|
||||||
Matrix::Transpose(inverseTransposeMatrix, inverseTransposeMatrix);
|
Matrix::Transpose(inverseTransposeMatrix, inverseTransposeMatrix);
|
||||||
|
|
||||||
|
// Transform blend shapes
|
||||||
|
for (auto& blendShape : BlendShapes)
|
||||||
|
{
|
||||||
|
const auto vv = blendShape.Vertices.Get();
|
||||||
|
for (int32 i = 0; i < blendShape.Vertices.Count(); i++)
|
||||||
|
{
|
||||||
|
auto& v = vv[i];
|
||||||
|
|
||||||
|
Float3 p = Positions[v.VertexIndex];
|
||||||
|
Float3 vp = p + v.PositionDelta;
|
||||||
|
Float3::Transform(vp, matrix, vp);
|
||||||
|
Float3::Transform(p, matrix, p);
|
||||||
|
v.PositionDelta = vp - p;
|
||||||
|
|
||||||
|
Float3 n = Normals[v.VertexIndex];
|
||||||
|
Float3 vn = n + v.NormalDelta;
|
||||||
|
vn.Normalize();
|
||||||
|
Float3::TransformNormal(vn, inverseTransposeMatrix, vn);
|
||||||
|
vn.Normalize();
|
||||||
|
Float3::TransformNormal(n, inverseTransposeMatrix, n);
|
||||||
|
n.Normalize();
|
||||||
|
v.NormalDelta = vn - n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Transform positions
|
// Transform positions
|
||||||
const auto pp = Positions.Get();
|
const auto pp = Positions.Get();
|
||||||
for (int32 i = 0; i < Positions.Count(); i++)
|
for (int32 i = 0; i < Positions.Count(); i++)
|
||||||
@@ -531,18 +556,6 @@ void MeshData::TransformBuffer(const Matrix& matrix)
|
|||||||
Float3::TransformNormal(t, inverseTransposeMatrix, t);
|
Float3::TransformNormal(t, inverseTransposeMatrix, t);
|
||||||
t.Normalize();
|
t.Normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform blend shapes
|
|
||||||
for (auto& blendShape : BlendShapes)
|
|
||||||
{
|
|
||||||
for (int32 i = 0; i < blendShape.Vertices.Count(); i++)
|
|
||||||
{
|
|
||||||
auto& v = blendShape.Vertices[i];
|
|
||||||
Float3::Transform(v.PositionDelta, matrix, v.PositionDelta);
|
|
||||||
Float3::TransformNormal(v.NormalDelta, inverseTransposeMatrix, v.NormalDelta);
|
|
||||||
v.NormalDelta.Normalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshData::NormalizeBlendWeights()
|
void MeshData::NormalizeBlendWeights()
|
||||||
@@ -550,9 +563,10 @@ void MeshData::NormalizeBlendWeights()
|
|||||||
ASSERT(Positions.Count() == BlendWeights.Count());
|
ASSERT(Positions.Count() == BlendWeights.Count());
|
||||||
for (int32 i = 0; i < Positions.Count(); i++)
|
for (int32 i = 0; i < Positions.Count(); i++)
|
||||||
{
|
{
|
||||||
const float sum = BlendWeights[i].SumValues();
|
Float4& weights = BlendWeights.Get()[i];
|
||||||
|
const float sum = weights.SumValues();
|
||||||
const float invSum = sum > ZeroTolerance ? 1.0f / sum : 0.0f;
|
const float invSum = sum > ZeroTolerance ? 1.0f / sum : 0.0f;
|
||||||
BlendWeights[i] *= invSum;
|
weights *= invSum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ SkinnedMesh::~SkinnedMesh()
|
|||||||
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkinnedMesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* ib, bool use16BitIndexBuffer)
|
bool SkinnedMesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* ib, bool use16BitIndexBuffer)
|
||||||
{
|
{
|
||||||
// Cache data
|
// Cache data
|
||||||
uint32 indicesCount = triangles * 3;
|
uint32 indicesCount = triangles * 3;
|
||||||
@@ -159,7 +159,7 @@ void SkinnedMesh::Unload()
|
|||||||
_use16BitIndexBuffer = false;
|
_use16BitIndexBuffer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices)
|
bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const void* ib, bool use16BitIndices)
|
||||||
{
|
{
|
||||||
auto model = (SkinnedModel*)_model;
|
auto model = (SkinnedModel*)_model;
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0Skinne
|
|||||||
{
|
{
|
||||||
// Calculate mesh bounds
|
// Calculate mesh bounds
|
||||||
BoundingBox bounds;
|
BoundingBox bounds;
|
||||||
BoundingBox::FromPoints((Float3*)vb, vertexCount, bounds);
|
BoundingBox::FromPoints((const Float3*)vb, vertexCount, bounds);
|
||||||
SetBounds(bounds);
|
SetBounds(bounds);
|
||||||
|
|
||||||
// Send event (actors using this model can update bounds, etc.)
|
// Send event (actors using this model can update bounds, etc.)
|
||||||
@@ -429,7 +429,7 @@ ScriptingObject* SkinnedMesh::GetParentModel()
|
|||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
|
|
||||||
template<typename IndexType>
|
template<typename IndexType>
|
||||||
bool UpdateMesh(SkinnedMesh* mesh, MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj)
|
bool UpdateMesh(SkinnedMesh* mesh, const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj)
|
||||||
{
|
{
|
||||||
auto model = mesh->GetSkinnedModel();
|
auto model = mesh->GetSkinnedModel();
|
||||||
ASSERT(model && model->IsVirtual() && verticesObj && trianglesObj && blendIndicesObj && blendWeightsObj);
|
ASSERT(model && model->IsVirtual() && verticesObj && trianglesObj && blendIndicesObj && blendWeightsObj);
|
||||||
@@ -505,12 +505,12 @@ bool UpdateMesh(SkinnedMesh* mesh, MArray* verticesObj, MArray* trianglesObj, MA
|
|||||||
return mesh->UpdateMesh(vertexCount, triangleCount, vb.Get(), ib);
|
return mesh->UpdateMesh(vertexCount, triangleCount, vb.Get(), ib);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkinnedMesh::UpdateMeshUInt(MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj)
|
bool SkinnedMesh::UpdateMeshUInt(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj)
|
||||||
{
|
{
|
||||||
return ::UpdateMesh<uint32>(this, verticesObj, trianglesObj, blendIndicesObj, blendWeightsObj, normalsObj, tangentsObj, uvObj);
|
return ::UpdateMesh<uint32>(this, verticesObj, trianglesObj, blendIndicesObj, blendWeightsObj, normalsObj, tangentsObj, uvObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkinnedMesh::UpdateMeshUShort(MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj)
|
bool SkinnedMesh::UpdateMeshUShort(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj)
|
||||||
{
|
{
|
||||||
return ::UpdateMesh<uint16>(this, verticesObj, trianglesObj, blendIndicesObj, blendWeightsObj, normalsObj, tangentsObj, uvObj);
|
return ::UpdateMesh<uint16>(this, verticesObj, trianglesObj, blendIndicesObj, blendWeightsObj, normalsObj, tangentsObj, uvObj);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public:
|
|||||||
/// <param name="ib">Index buffer data</param>
|
/// <param name="ib">Index buffer data</param>
|
||||||
/// <param name="use16BitIndexBuffer">True if use 16 bit indices for the index buffer (true: uint16, false: uint32).</param>
|
/// <param name="use16BitIndexBuffer">True if use 16 bit indices for the index buffer (true: uint16, false: uint32).</param>
|
||||||
/// <returns>True if cannot load data, otherwise false.</returns>
|
/// <returns>True if cannot load data, otherwise false.</returns>
|
||||||
bool Load(uint32 vertices, uint32 triangles, void* vb0, void* ib, bool use16BitIndexBuffer);
|
bool Load(uint32 vertices, uint32 triangles, const void* vb0, const void* ib, bool use16BitIndexBuffer);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unloads the mesh data (vertex buffers and cache). The opposite to Load.
|
/// Unloads the mesh data (vertex buffers and cache). The opposite to Load.
|
||||||
@@ -92,7 +92,7 @@ public:
|
|||||||
/// <param name="vb">The vertex buffer data.</param>
|
/// <param name="vb">The vertex buffer data.</param>
|
||||||
/// <param name="ib">The index buffer in clockwise order.</param>
|
/// <param name="ib">The index buffer in clockwise order.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, int32* ib)
|
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const int32* ib)
|
||||||
{
|
{
|
||||||
return UpdateMesh(vertexCount, triangleCount, vb, ib, false);
|
return UpdateMesh(vertexCount, triangleCount, vb, ib, false);
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ public:
|
|||||||
/// <param name="vb">The vertex buffer data.</param>
|
/// <param name="vb">The vertex buffer data.</param>
|
||||||
/// <param name="ib">The index buffer in clockwise order.</param>
|
/// <param name="ib">The index buffer in clockwise order.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint32* ib)
|
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const uint32* ib)
|
||||||
{
|
{
|
||||||
return UpdateMesh(vertexCount, triangleCount, vb, ib, false);
|
return UpdateMesh(vertexCount, triangleCount, vb, ib, false);
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ public:
|
|||||||
/// <param name="vb">The vertex buffer data.</param>
|
/// <param name="vb">The vertex buffer data.</param>
|
||||||
/// <param name="ib">The index buffer, clockwise order.</param>
|
/// <param name="ib">The index buffer, clockwise order.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint16* ib)
|
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const uint16* ib)
|
||||||
{
|
{
|
||||||
return UpdateMesh(vertexCount, triangleCount, vb, ib, true);
|
return UpdateMesh(vertexCount, triangleCount, vb, ib, true);
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ public:
|
|||||||
/// <param name="ib">The index buffer in clockwise order.</param>
|
/// <param name="ib">The index buffer in clockwise order.</param>
|
||||||
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
|
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices);
|
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const void* ib, bool use16BitIndices);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -188,8 +188,8 @@ private:
|
|||||||
// Internal bindings
|
// Internal bindings
|
||||||
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
|
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
API_FUNCTION(NoProxy) bool UpdateMeshUInt(MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj);
|
API_FUNCTION(NoProxy) bool UpdateMeshUInt(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj);
|
||||||
API_FUNCTION(NoProxy) bool UpdateMeshUShort(MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj);
|
API_FUNCTION(NoProxy) bool UpdateMeshUShort(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj);
|
||||||
API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI);
|
API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI);
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -131,14 +131,14 @@ void GPUTextureViewVulkan::Release()
|
|||||||
{
|
{
|
||||||
Device->OnImageViewDestroy(ViewFramebuffer);
|
Device->OnImageViewDestroy(ViewFramebuffer);
|
||||||
Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewFramebuffer);
|
Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewFramebuffer);
|
||||||
ViewFramebuffer = VK_NULL_HANDLE;
|
|
||||||
}
|
}
|
||||||
|
ViewFramebuffer = VK_NULL_HANDLE;
|
||||||
if (ViewSRV != View && ViewSRV != VK_NULL_HANDLE)
|
if (ViewSRV != View && ViewSRV != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
Device->OnImageViewDestroy(ViewSRV);
|
Device->OnImageViewDestroy(ViewSRV);
|
||||||
Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewSRV);
|
Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewSRV);
|
||||||
ViewSRV = VK_NULL_HANDLE;
|
|
||||||
}
|
}
|
||||||
|
ViewSRV = VK_NULL_HANDLE;
|
||||||
|
|
||||||
Device->OnImageViewDestroy(View);
|
Device->OnImageViewDestroy(View);
|
||||||
Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, View);
|
Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, View);
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ void AnimatedModel::UpdateAnimation()
|
|||||||
|| !IsActiveInHierarchy()
|
|| !IsActiveInHierarchy()
|
||||||
|| SkinnedModel == nullptr
|
|| SkinnedModel == nullptr
|
||||||
|| !SkinnedModel->IsLoaded()
|
|| !SkinnedModel->IsLoaded()
|
||||||
|| _lastUpdateFrame == Engine::FrameCount
|
|| _lastUpdateFrame == Engine::UpdateCount
|
||||||
|| _masterPose)
|
|| _masterPose)
|
||||||
return;
|
return;
|
||||||
_lastUpdateFrame = Engine::FrameCount;
|
_lastUpdateFrame = Engine::UpdateCount;
|
||||||
|
|
||||||
if (AnimationGraph && AnimationGraph->IsLoaded() && AnimationGraph->Graph.IsReady())
|
if (AnimationGraph && AnimationGraph->IsLoaded() && AnimationGraph->Graph.IsReady())
|
||||||
{
|
{
|
||||||
@@ -113,7 +113,7 @@ void AnimatedModel::PreInitSkinningData()
|
|||||||
for (int32 boneIndex = 0; boneIndex < bonesCount; boneIndex++)
|
for (int32 boneIndex = 0; boneIndex < bonesCount; boneIndex++)
|
||||||
{
|
{
|
||||||
auto& bone = skeleton.Bones[boneIndex];
|
auto& bone = skeleton.Bones[boneIndex];
|
||||||
identityMatrices[boneIndex] = bone.OffsetMatrix * GraphInstance.NodesPose[bone.NodeIndex];
|
identityMatrices.Get()[boneIndex] = bone.OffsetMatrix * GraphInstance.NodesPose[bone.NodeIndex];
|
||||||
}
|
}
|
||||||
_skinningData.SetData(identityMatrices.Get(), true);
|
_skinningData.SetData(identityMatrices.Get(), true);
|
||||||
|
|
||||||
@@ -640,7 +640,7 @@ void AnimatedModel::RunBlendShapeDeformer(const MeshBase* mesh, MeshDeformationD
|
|||||||
ASSERT_LOW_LAYER(blendShapeVertex.VertexIndex < vertexCount);
|
ASSERT_LOW_LAYER(blendShapeVertex.VertexIndex < vertexCount);
|
||||||
VB0SkinnedElementType& vertex = *(data + blendShapeVertex.VertexIndex);
|
VB0SkinnedElementType& vertex = *(data + blendShapeVertex.VertexIndex);
|
||||||
vertex.Position = vertex.Position + blendShapeVertex.PositionDelta * q.Second;
|
vertex.Position = vertex.Position + blendShapeVertex.PositionDelta * q.Second;
|
||||||
Float3 normal = (vertex.Normal.ToFloat3() * 2.0f - 1.0f) + blendShapeVertex.NormalDelta;
|
Float3 normal = (vertex.Normal.ToFloat3() * 2.0f - 1.0f) + blendShapeVertex.NormalDelta * q.Second;
|
||||||
vertex.Normal = normal * 0.5f + 0.5f;
|
vertex.Normal = normal * 0.5f + 0.5f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ void StaticModel::Draw(RenderContext& renderContext)
|
|||||||
draw.World = &world;
|
draw.World = &world;
|
||||||
draw.DrawState = &_drawState;
|
draw.DrawState = &_drawState;
|
||||||
draw.Deformation = _deformation;
|
draw.Deformation = _deformation;
|
||||||
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
|
draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr;
|
||||||
draw.LightmapUVs = &Lightmap.UVsArea;
|
draw.LightmapUVs = &Lightmap.UVsArea;
|
||||||
draw.Flags = _staticFlags;
|
draw.Flags = _staticFlags;
|
||||||
draw.DrawModes = DrawModes;
|
draw.DrawModes = DrawModes;
|
||||||
@@ -380,7 +380,7 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
|
|||||||
draw.World = &world;
|
draw.World = &world;
|
||||||
draw.DrawState = &_drawState;
|
draw.DrawState = &_drawState;
|
||||||
draw.Deformation = _deformation;
|
draw.Deformation = _deformation;
|
||||||
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
|
draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr;
|
||||||
draw.LightmapUVs = &Lightmap.UVsArea;
|
draw.LightmapUVs = &Lightmap.UVsArea;
|
||||||
draw.Flags = _staticFlags;
|
draw.Flags = _staticFlags;
|
||||||
draw.DrawModes = DrawModes;
|
draw.DrawModes = DrawModes;
|
||||||
|
|||||||
@@ -628,6 +628,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
|
|||||||
LOG(Warning, "Could not build Detour navmesh.");
|
LOG(Warning, "Could not build Detour navmesh.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
ASSERT_LOW_LAYER(navDataSize > 4 && *(uint32*)navData == DT_NAVMESH_MAGIC); // Sanity check for Detour header
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Navigation.CreateTile");
|
PROFILE_CPU_NAMED("Navigation.CreateTile");
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ void NavMeshData::Save(WriteStream& stream)
|
|||||||
// Write tiles
|
// Write tiles
|
||||||
for (int32 tileIndex = 0; tileIndex < Tiles.Count(); tileIndex++)
|
for (int32 tileIndex = 0; tileIndex < Tiles.Count(); tileIndex++)
|
||||||
{
|
{
|
||||||
auto& tile = Tiles[tileIndex];
|
auto& tile = Tiles.Get()[tileIndex];
|
||||||
|
|
||||||
// Write tile header
|
// Write tile header
|
||||||
NavMeshTileDataHeader tileHeader;
|
NavMeshTileDataHeader tileHeader;
|
||||||
@@ -41,7 +41,6 @@ void NavMeshData::Save(WriteStream& stream)
|
|||||||
|
|
||||||
bool NavMeshData::Load(BytesContainer& data, bool copyData)
|
bool NavMeshData::Load(BytesContainer& data, bool copyData)
|
||||||
{
|
{
|
||||||
// No data
|
|
||||||
if (data.Length() < sizeof(NavMeshDataHeader))
|
if (data.Length() < sizeof(NavMeshDataHeader))
|
||||||
{
|
{
|
||||||
LOG(Warning, "No valid navmesh data.");
|
LOG(Warning, "No valid navmesh data.");
|
||||||
@@ -50,7 +49,7 @@ bool NavMeshData::Load(BytesContainer& data, bool copyData)
|
|||||||
MemoryReadStream stream(data.Get(), data.Length());
|
MemoryReadStream stream(data.Get(), data.Length());
|
||||||
|
|
||||||
// Read header
|
// Read header
|
||||||
const auto header = stream.Move<NavMeshDataHeader>(1);
|
const auto header = stream.Move<NavMeshDataHeader>();
|
||||||
if (header->Version != 1)
|
if (header->Version != 1)
|
||||||
{
|
{
|
||||||
LOG(Warning, "Invalid valid navmesh data version {0}.", header->Version);
|
LOG(Warning, "Invalid valid navmesh data version {0}.", header->Version);
|
||||||
@@ -67,10 +66,10 @@ bool NavMeshData::Load(BytesContainer& data, bool copyData)
|
|||||||
// Read tiles
|
// Read tiles
|
||||||
for (int32 tileIndex = 0; tileIndex < Tiles.Count(); tileIndex++)
|
for (int32 tileIndex = 0; tileIndex < Tiles.Count(); tileIndex++)
|
||||||
{
|
{
|
||||||
auto& tile = Tiles[tileIndex];
|
auto& tile = Tiles.Get()[tileIndex];
|
||||||
|
|
||||||
// Read tile header
|
// Read tile header
|
||||||
const auto tileHeader = stream.Move<NavMeshTileDataHeader>(1);
|
const auto tileHeader = stream.Move<NavMeshTileDataHeader>();
|
||||||
if (tileHeader->DataSize <= 0)
|
if (tileHeader->DataSize <= 0)
|
||||||
{
|
{
|
||||||
LOG(Warning, "Invalid navmesh tile data.");
|
LOG(Warning, "Invalid navmesh tile data.");
|
||||||
@@ -83,13 +82,9 @@ bool NavMeshData::Load(BytesContainer& data, bool copyData)
|
|||||||
// Read tile data
|
// Read tile data
|
||||||
const auto tileData = stream.Move<byte>(tileHeader->DataSize);
|
const auto tileData = stream.Move<byte>(tileHeader->DataSize);
|
||||||
if (copyData)
|
if (copyData)
|
||||||
{
|
|
||||||
tile.Data.Copy(tileData, tileHeader->DataSize);
|
tile.Data.Copy(tileData, tileHeader->DataSize);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
tile.Data.Link(tileData, tileHeader->DataSize);
|
tile.Data.Link(tileData, tileHeader->DataSize);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -60,17 +60,12 @@ bool NavMeshRuntime::FindDistanceToWall(const Vector3& startPosition, NavMeshHit
|
|||||||
Float3::Transform(startPosition, Properties.Rotation, startPositionNavMesh);
|
Float3::Transform(startPosition, Properties.Rotation, startPositionNavMesh);
|
||||||
|
|
||||||
dtPolyRef startPoly = 0;
|
dtPolyRef startPoly = 0;
|
||||||
query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr);
|
if (!dtStatusSucceed(query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr)))
|
||||||
if (!startPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
Float3 hitPosition, hitNormal;
|
Float3 hitPosition, hitNormal;
|
||||||
if (!dtStatusSucceed(query->findDistanceToWall(startPoly, &startPositionNavMesh.X, maxDistance, &filter, &hitInfo.Distance, &hitPosition.X, &hitNormal.X)))
|
if (!dtStatusSucceed(query->findDistanceToWall(startPoly, &startPositionNavMesh.X, maxDistance, &filter, &hitInfo.Distance, &hitPosition.X, &hitNormal.X)))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
Quaternion invRotation;
|
Quaternion invRotation;
|
||||||
Quaternion::Invert(Properties.Rotation, invRotation);
|
Quaternion::Invert(Properties.Rotation, invRotation);
|
||||||
@@ -98,17 +93,11 @@ bool NavMeshRuntime::FindPath(const Vector3& startPosition, const Vector3& endPo
|
|||||||
Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh);
|
Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh);
|
||||||
|
|
||||||
dtPolyRef startPoly = 0;
|
dtPolyRef startPoly = 0;
|
||||||
query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr);
|
if (!dtStatusSucceed(query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr)))
|
||||||
if (!startPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
dtPolyRef endPoly = 0;
|
dtPolyRef endPoly = 0;
|
||||||
query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr);
|
if (!dtStatusSucceed(query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr)))
|
||||||
if (!endPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
dtPolyRef path[NAV_MESH_PATH_MAX_SIZE];
|
dtPolyRef path[NAV_MESH_PATH_MAX_SIZE];
|
||||||
int32 pathSize;
|
int32 pathSize;
|
||||||
@@ -166,30 +155,19 @@ bool NavMeshRuntime::TestPath(const Vector3& startPosition, const Vector3& endPo
|
|||||||
Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh);
|
Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh);
|
||||||
|
|
||||||
dtPolyRef startPoly = 0;
|
dtPolyRef startPoly = 0;
|
||||||
query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr);
|
if (!dtStatusSucceed(query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr)))
|
||||||
if (!startPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
dtPolyRef endPoly = 0;
|
dtPolyRef endPoly = 0;
|
||||||
query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr);
|
if (!dtStatusSucceed(query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr)))
|
||||||
if (!endPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
dtPolyRef path[NAV_MESH_PATH_MAX_SIZE];
|
dtPolyRef path[NAV_MESH_PATH_MAX_SIZE];
|
||||||
int32 pathSize;
|
int32 pathSize;
|
||||||
const auto findPathStatus = query->findPath(startPoly, endPoly, &startPositionNavMesh.X, &endPositionNavMesh.X, &filter, path, &pathSize, NAV_MESH_PATH_MAX_SIZE);
|
const auto findPathStatus = query->findPath(startPoly, endPoly, &startPositionNavMesh.X, &endPositionNavMesh.X, &filter, path, &pathSize, NAV_MESH_PATH_MAX_SIZE);
|
||||||
if (dtStatusFailed(findPathStatus))
|
if (dtStatusFailed(findPathStatus))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (dtStatusDetail(findPathStatus, DT_PARTIAL_RESULT))
|
if (dtStatusDetail(findPathStatus, DT_PARTIAL_RESULT))
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -210,11 +188,8 @@ bool NavMeshRuntime::FindClosestPoint(const Vector3& point, Vector3& result) con
|
|||||||
|
|
||||||
dtPolyRef startPoly = 0;
|
dtPolyRef startPoly = 0;
|
||||||
Float3 nearestPt;
|
Float3 nearestPt;
|
||||||
query->findNearestPoly(&pointNavMesh.X, &extent.X, &filter, &startPoly, &nearestPt.X);
|
if (!dtStatusSucceed(query->findNearestPoly(&pointNavMesh.X, &extent.X, &filter, &startPoly, &nearestPt.X)))
|
||||||
if (!startPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
Quaternion invRotation;
|
Quaternion invRotation;
|
||||||
Quaternion::Invert(Properties.Rotation, invRotation);
|
Quaternion::Invert(Properties.Rotation, invRotation);
|
||||||
@@ -235,11 +210,8 @@ bool NavMeshRuntime::FindRandomPoint(Vector3& result) const
|
|||||||
|
|
||||||
dtPolyRef randomPoly = 0;
|
dtPolyRef randomPoly = 0;
|
||||||
Float3 randomPt;
|
Float3 randomPt;
|
||||||
query->findRandomPoint(&filter, Random::Rand, &randomPoly, &randomPt.X);
|
if (!dtStatusSucceed(query->findRandomPoint(&filter, Random::Rand, &randomPoly, &randomPt.X)))
|
||||||
if (!randomPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
Quaternion invRotation;
|
Quaternion invRotation;
|
||||||
Quaternion::Invert(Properties.Rotation, invRotation);
|
Quaternion::Invert(Properties.Rotation, invRotation);
|
||||||
@@ -257,25 +229,19 @@ bool NavMeshRuntime::FindRandomPointAroundCircle(const Vector3& center, float ra
|
|||||||
|
|
||||||
dtQueryFilter filter;
|
dtQueryFilter filter;
|
||||||
InitFilter(filter);
|
InitFilter(filter);
|
||||||
Float3 extent = Properties.DefaultQueryExtent;
|
Float3 extent(radius);
|
||||||
|
|
||||||
Float3 centerNavMesh;
|
Float3 centerNavMesh;
|
||||||
Float3::Transform(center, Properties.Rotation, centerNavMesh);
|
Float3::Transform(center, Properties.Rotation, centerNavMesh);
|
||||||
|
|
||||||
dtPolyRef centerPoly = 0;
|
dtPolyRef centerPoly = 0;
|
||||||
query->findNearestPoly(¢erNavMesh.X, &extent.X, &filter, ¢erPoly, nullptr);
|
if (!dtStatusSucceed(query->findNearestPoly(¢erNavMesh.X, &extent.X, &filter, ¢erPoly, nullptr)))
|
||||||
if (!centerPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
dtPolyRef randomPoly = 0;
|
dtPolyRef randomPoly = 0;
|
||||||
Float3 randomPt;
|
Float3 randomPt;
|
||||||
query->findRandomPointAroundCircle(centerPoly, ¢erNavMesh.X, radius, &filter, Random::Rand, &randomPoly, &randomPt.X);
|
if (!dtStatusSucceed(query->findRandomPointAroundCircle(centerPoly, ¢erNavMesh.X, radius, &filter, Random::Rand, &randomPoly, &randomPt.X)))
|
||||||
if (!randomPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
Quaternion invRotation;
|
Quaternion invRotation;
|
||||||
Quaternion::Invert(Properties.Rotation, invRotation);
|
Quaternion::Invert(Properties.Rotation, invRotation);
|
||||||
@@ -300,11 +266,8 @@ bool NavMeshRuntime::RayCast(const Vector3& startPosition, const Vector3& endPos
|
|||||||
Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh);
|
Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh);
|
||||||
|
|
||||||
dtPolyRef startPoly = 0;
|
dtPolyRef startPoly = 0;
|
||||||
query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr);
|
if (!dtStatusSucceed(query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr)))
|
||||||
if (!startPoly)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
dtRaycastHit hit;
|
dtRaycastHit hit;
|
||||||
hit.path = nullptr;
|
hit.path = nullptr;
|
||||||
@@ -361,14 +324,6 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount)
|
|||||||
// Ensure to have size assigned
|
// Ensure to have size assigned
|
||||||
ASSERT(_tileSize != 0);
|
ASSERT(_tileSize != 0);
|
||||||
|
|
||||||
// Allocate navmesh and initialize the default query
|
|
||||||
if (!_navMesh)
|
|
||||||
_navMesh = dtAllocNavMesh();
|
|
||||||
if (dtStatusFailed(_navMeshQuery->init(_navMesh, MAX_NODES)))
|
|
||||||
{
|
|
||||||
LOG(Error, "Failed to initialize navmesh {0}.", Properties.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare parameters
|
// Prepare parameters
|
||||||
dtNavMeshParams params;
|
dtNavMeshParams params;
|
||||||
params.orig[0] = 0.0f;
|
params.orig[0] = 0.0f;
|
||||||
@@ -381,11 +336,17 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount)
|
|||||||
params.maxPolys = 1 << (22 - tilesBits);
|
params.maxPolys = 1 << (22 - tilesBits);
|
||||||
|
|
||||||
// Initialize nav mesh
|
// Initialize nav mesh
|
||||||
|
if (!_navMesh)
|
||||||
|
_navMesh = dtAllocNavMesh();
|
||||||
if (dtStatusFailed(_navMesh->init(¶ms)))
|
if (dtStatusFailed(_navMesh->init(¶ms)))
|
||||||
{
|
{
|
||||||
LOG(Error, "Navmesh {0} init failed.", Properties.Name);
|
LOG(Error, "Navmesh {0} init failed", Properties.Name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (dtStatusFailed(_navMeshQuery->init(_navMesh, MAX_NODES)))
|
||||||
|
{
|
||||||
|
LOG(Error, "Navmesh query {0} init failed", Properties.Name);
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare tiles container
|
// Prepare tiles container
|
||||||
_tiles.EnsureCapacity(newCapacity);
|
_tiles.EnsureCapacity(newCapacity);
|
||||||
@@ -405,7 +366,7 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount)
|
|||||||
const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr);
|
const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr);
|
||||||
if (dtStatusFailed(result))
|
if (dtStatusFailed(result))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Could not add tile to navmesh {0} (error: {1}).", Properties.Name, result & ~DT_FAILURE);
|
LOG(Warning, "Could not add tile ({2}x{3}, layer {4}) to navmesh {0} (error: {1})", Properties.Name, result & ~DT_FAILURE, tile.X, tile.Y, tile.Layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -490,13 +451,11 @@ void NavMeshRuntime::RemoveTile(int32 x, int32 y, int32 layer)
|
|||||||
|
|
||||||
const auto tileRef = _navMesh->getTileRefAt(x, y, layer);
|
const auto tileRef = _navMesh->getTileRefAt(x, y, layer);
|
||||||
if (tileRef == 0)
|
if (tileRef == 0)
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr)))
|
if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr)))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name);
|
LOG(Warning, "Failed to remove tile ({1}x{2}, layer {3}) from navmesh {0}", Properties.Name, x, y, layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32 i = 0; i < _tiles.Count(); i++)
|
for (int32 i = 0; i < _tiles.Count(); i++)
|
||||||
@@ -532,7 +491,7 @@ void NavMeshRuntime::RemoveTiles(bool (*prediction)(const NavMeshRuntime* navMes
|
|||||||
{
|
{
|
||||||
if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr)))
|
if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr)))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name);
|
LOG(Warning, "Failed to remove tile ({1}x{2}, layer {3}) from navmesh {0}", Properties.Name, tile.X, tile.Y, tile.Layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,7 +627,7 @@ void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData
|
|||||||
// Remove any existing tile at that location
|
// Remove any existing tile at that location
|
||||||
if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr)))
|
if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr)))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name);
|
LOG(Warning, "Failed to remove tile from navmesh {0}", Properties.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reuse tile data container
|
// Reuse tile data container
|
||||||
@@ -712,6 +671,6 @@ void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData
|
|||||||
const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr);
|
const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr);
|
||||||
if (dtStatusFailed(result))
|
if (dtStatusFailed(result))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Could not add tile to navmesh {0} (error: {1}).", Properties.Name, result & ~DT_FAILURE);
|
LOG(Warning, "Could not add tile ({2}x{3}, layer {4}) to navmesh {0} (error: {1})", Properties.Name, result & ~DT_FAILURE, tileData.PosX, tileData.PosY, tileData.Layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ public:
|
|||||||
/// <param name="point">The source point.</param>
|
/// <param name="point">The source point.</param>
|
||||||
/// <param name="result">The result position on the navmesh (valid only if method returns true).</param>
|
/// <param name="result">The result position on the navmesh (valid only if method returns true).</param>
|
||||||
/// <returns>True if found valid location on the navmesh, otherwise false.</returns>
|
/// <returns>True if found valid location on the navmesh, otherwise false.</returns>
|
||||||
API_FUNCTION() bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const
|
API_FUNCTION() DEPRECATED bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const
|
||||||
{
|
{
|
||||||
return FindClosestPoint(point, result);
|
return FindClosestPoint(point, result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -441,5 +441,23 @@ namespace FlaxEngine.Networking
|
|||||||
ReadBytes((byte*)&value, sizeof(bool));
|
ReadBytes((byte*)&value, sizeof(bool));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the <see cref="INetworkSerializable"/> object data to the stream. Object has to be allocated.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The serializable object.</param>
|
||||||
|
public void Write(INetworkSerializable obj)
|
||||||
|
{
|
||||||
|
obj.Serialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the <see cref="INetworkSerializable"/> object data from the stream. Object has to be allocated.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The serializable object.</param>
|
||||||
|
public void Read(INetworkSerializable obj)
|
||||||
|
{
|
||||||
|
obj.Deserialize(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public:
|
|||||||
/// <summary>
|
/// <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.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AssetReference<Asset> Assets[14];
|
Array<AssetReference<Asset>> Assets;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ public:
|
|||||||
// Get Gameplay Global
|
// Get Gameplay Global
|
||||||
case GRAPH_NODE_MAKE_TYPE(7, 16):
|
case GRAPH_NODE_MAKE_TYPE(7, 16):
|
||||||
{
|
{
|
||||||
|
node->Assets.Resize(1);
|
||||||
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[0]);
|
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[0]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -296,6 +297,7 @@ public:
|
|||||||
break;
|
break;
|
||||||
// Particle Emitter Function
|
// Particle Emitter Function
|
||||||
case GRAPH_NODE_MAKE_TYPE(14, 300):
|
case GRAPH_NODE_MAKE_TYPE(14, 300):
|
||||||
|
node->Assets.Resize(1);
|
||||||
InitParticleEmitterFunctionCall((Guid)node->Values[0], node->Assets[0], node->UsesParticleData, Layout);
|
InitParticleEmitterFunctionCall((Guid)node->Values[0], node->Assets[0], node->UsesParticleData, Layout);
|
||||||
break;
|
break;
|
||||||
// Particle Index
|
// Particle Index
|
||||||
@@ -447,6 +449,7 @@ public:
|
|||||||
// Sprite Rendering
|
// Sprite Rendering
|
||||||
case GRAPH_NODE_MAKE_TYPE(15, 400):
|
case GRAPH_NODE_MAKE_TYPE(15, 400):
|
||||||
{
|
{
|
||||||
|
node->Assets.Resize(1);
|
||||||
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[2]);
|
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[2]);
|
||||||
USE_ATTRIBUTE(Position, Float3, 0);
|
USE_ATTRIBUTE(Position, Float3, 0);
|
||||||
USE_ATTRIBUTE(Rotation, Float3, 1);
|
USE_ATTRIBUTE(Rotation, Float3, 1);
|
||||||
@@ -484,6 +487,7 @@ public:
|
|||||||
// Model Rendering
|
// Model Rendering
|
||||||
case GRAPH_NODE_MAKE_TYPE(15, 403):
|
case GRAPH_NODE_MAKE_TYPE(15, 403):
|
||||||
{
|
{
|
||||||
|
node->Assets.Resize(2);
|
||||||
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[2]);
|
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[2]);
|
||||||
node->Assets[1] = Content::LoadAsync<Asset>((Guid)node->Values[3]);
|
node->Assets[1] = Content::LoadAsync<Asset>((Guid)node->Values[3]);
|
||||||
USE_ATTRIBUTE(Position, Float3, 0);
|
USE_ATTRIBUTE(Position, Float3, 0);
|
||||||
@@ -494,6 +498,7 @@ public:
|
|||||||
// Ribbon Rendering
|
// Ribbon Rendering
|
||||||
case GRAPH_NODE_MAKE_TYPE(15, 404):
|
case GRAPH_NODE_MAKE_TYPE(15, 404):
|
||||||
{
|
{
|
||||||
|
node->Assets.Resize(1);
|
||||||
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[2]);
|
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[2]);
|
||||||
USE_ATTRIBUTE(Position, Float3, 0);
|
USE_ATTRIBUTE(Position, Float3, 0);
|
||||||
// TODO: add support for custom sorting key - not only by age
|
// TODO: add support for custom sorting key - not only by age
|
||||||
@@ -503,6 +508,7 @@ public:
|
|||||||
// Volumetric Fog Rendering
|
// Volumetric Fog Rendering
|
||||||
case GRAPH_NODE_MAKE_TYPE(15, 405):
|
case GRAPH_NODE_MAKE_TYPE(15, 405):
|
||||||
{
|
{
|
||||||
|
node->Assets.Resize(1);
|
||||||
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[2]);
|
node->Assets[0] = Content::LoadAsync<Asset>((Guid)node->Values[2]);
|
||||||
USE_ATTRIBUTE(Position, Float3, 0);
|
USE_ATTRIBUTE(Position, Float3, 0);
|
||||||
USE_ATTRIBUTE(Radius, Float, 1);
|
USE_ATTRIBUTE(Radius, Float, 1);
|
||||||
|
|||||||
@@ -270,11 +270,11 @@ void ParticleEffect::UpdateSimulation(bool singleFrame)
|
|||||||
if (!IsActiveInHierarchy()
|
if (!IsActiveInHierarchy()
|
||||||
|| ParticleSystem == nullptr
|
|| ParticleSystem == nullptr
|
||||||
|| !ParticleSystem->IsLoaded()
|
|| !ParticleSystem->IsLoaded()
|
||||||
|| _lastUpdateFrame == Engine::FrameCount)
|
|| _lastUpdateFrame == Engine::UpdateCount)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Request update
|
// Request update
|
||||||
_lastUpdateFrame = Engine::FrameCount;
|
_lastUpdateFrame = Engine::UpdateCount;
|
||||||
_lastMinDstSqr = MAX_Real;
|
_lastMinDstSqr = MAX_Real;
|
||||||
if (singleFrame)
|
if (singleFrame)
|
||||||
Instance.LastUpdateTime = (UseTimeScale ? Time::Update.Time : Time::Update.UnscaledTime).GetTotalSeconds();
|
Instance.LastUpdateTime = (UseTimeScale ? Time::Update.Time : Time::Update.UnscaledTime).GetTotalSeconds();
|
||||||
@@ -371,7 +371,7 @@ void ParticleEffect::Sync()
|
|||||||
|
|
||||||
SceneRenderTask* ParticleEffect::GetRenderTask() const
|
SceneRenderTask* ParticleEffect::GetRenderTask() const
|
||||||
{
|
{
|
||||||
const uint64 minFrame = Engine::FrameCount - 2;
|
const uint64 minFrame = Engine::UpdateCount - 2;
|
||||||
|
|
||||||
// Custom task
|
// Custom task
|
||||||
const auto customViewRenderTask = CustomViewRenderTask.Get();
|
const auto customViewRenderTask = CustomViewRenderTask.Get();
|
||||||
|
|||||||
@@ -988,7 +988,7 @@ void DrawBatch(int32 startIndex, int32 count)
|
|||||||
MaterialBase::BindParameters bindParams(Context, *(RenderContext*)nullptr);
|
MaterialBase::BindParameters bindParams(Context, *(RenderContext*)nullptr);
|
||||||
Render2D::CustomData customData;
|
Render2D::CustomData customData;
|
||||||
customData.ViewProjection = ViewProjection;
|
customData.ViewProjection = ViewProjection;
|
||||||
customData.ViewSize = Float2(d.AsMaterial.Width, d.AsMaterial.Height);
|
customData.ViewSize = Float2::One;
|
||||||
bindParams.CustomData = &customData;
|
bindParams.CustomData = &customData;
|
||||||
material->Bind(bindParams);
|
material->Bind(bindParams);
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ void EyeAdaptationPass::Render(RenderContext& renderContext, GPUTexture* colorBu
|
|||||||
bool dropHistory = renderContext.Buffers->LastEyeAdaptationTime < ZeroTolerance || renderContext.Task->IsCameraCut;
|
bool dropHistory = renderContext.Buffers->LastEyeAdaptationTime < ZeroTolerance || renderContext.Task->IsCameraCut;
|
||||||
const float time = Time::Draw.UnscaledTime.GetTotalSeconds();
|
const float time = Time::Draw.UnscaledTime.GetTotalSeconds();
|
||||||
//const float frameDelta = Time::ElapsedGameTime.GetTotalSeconds();
|
//const float frameDelta = Time::ElapsedGameTime.GetTotalSeconds();
|
||||||
const float frameDelta = time - renderContext.Buffers->LastEyeAdaptationTime;
|
const float frameDelta = Math::Clamp(time - renderContext.Buffers->LastEyeAdaptationTime, 0.0f, 1.0f);
|
||||||
renderContext.Buffers->LastEyeAdaptationTime = 0.0f;
|
renderContext.Buffers->LastEyeAdaptationTime = 0.0f;
|
||||||
if ((view.Flags & ViewFlags::EyeAdaptation) == ViewFlags::None || settings.Mode == EyeAdaptationMode::None || checkIfSkipPass())
|
if ((view.Flags & ViewFlags::EyeAdaptation) == ViewFlags::None || settings.Mode == EyeAdaptationMode::None || checkIfSkipPass())
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -109,6 +109,45 @@ namespace
|
|||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
bool LastBinariesLoadTriggeredCompilation = false;
|
bool LastBinariesLoadTriggeredCompilation = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void ReleaseObjects(bool gameOnly)
|
||||||
|
{
|
||||||
|
// Flush objects already enqueued objects to delete
|
||||||
|
ObjectsRemovalService::Flush();
|
||||||
|
|
||||||
|
// Give GC a try to cleanup old user objects and the other mess
|
||||||
|
MCore::GC::Collect();
|
||||||
|
MCore::GC::WaitForPendingFinalizers();
|
||||||
|
|
||||||
|
// Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload)
|
||||||
|
const auto flaxModule = GetBinaryModuleFlaxEngine();
|
||||||
|
_objectsLocker.Lock();
|
||||||
|
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
|
||||||
|
{
|
||||||
|
auto obj = i->Value;
|
||||||
|
if (gameOnly && obj->GetTypeHandle().Module == flaxModule)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||||
|
LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName));
|
||||||
|
#endif
|
||||||
|
obj->OnScriptingDispose();
|
||||||
|
}
|
||||||
|
_objectsLocker.Unlock();
|
||||||
|
|
||||||
|
// Release assets sourced from game assemblies
|
||||||
|
Array<Asset*> assets = Content::GetAssets();
|
||||||
|
for (auto asset : assets)
|
||||||
|
{
|
||||||
|
if (asset->GetTypeHandle().Module == flaxModule)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->DeleteObject();
|
||||||
|
}
|
||||||
|
ObjectsRemovalService::Flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Delegate<BinaryModule*> Scripting::BinaryModuleLoaded;
|
Delegate<BinaryModule*> Scripting::BinaryModuleLoaded;
|
||||||
@@ -566,36 +605,8 @@ void Scripting::Release()
|
|||||||
// Fire event
|
// Fire event
|
||||||
ScriptsUnload();
|
ScriptsUnload();
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
ObjectsRemovalService::Flush();
|
|
||||||
|
|
||||||
// Cleanup some managed objects
|
|
||||||
MCore::GC::Collect();
|
|
||||||
MCore::GC::WaitForPendingFinalizers();
|
|
||||||
|
|
||||||
// Release managed objects instances for persistent objects (assets etc.)
|
// Release managed objects instances for persistent objects (assets etc.)
|
||||||
_objectsLocker.Lock();
|
ReleaseObjects(false);
|
||||||
{
|
|
||||||
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
|
|
||||||
{
|
|
||||||
auto obj = i->Value;
|
|
||||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
|
||||||
LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName));
|
|
||||||
#endif
|
|
||||||
obj->OnScriptingDispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_objectsLocker.Unlock();
|
|
||||||
|
|
||||||
// Release assets sourced from game assemblies
|
|
||||||
const auto flaxModule = GetBinaryModuleFlaxEngine();
|
|
||||||
for (auto asset : Content::GetAssets())
|
|
||||||
{
|
|
||||||
if (asset->GetTypeHandle().Module == flaxModule)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
asset->DeleteObjectNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine();
|
auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine();
|
||||||
onEngineUnloading(flaxEngineModule->Assembly);
|
onEngineUnloading(flaxEngineModule->Assembly);
|
||||||
@@ -673,39 +684,8 @@ void Scripting::Reload(bool canTriggerSceneReload)
|
|||||||
LOG(Info, "Start user scripts reload");
|
LOG(Info, "Start user scripts reload");
|
||||||
ScriptsReloading();
|
ScriptsReloading();
|
||||||
|
|
||||||
// Flush cache (some objects may be deleted after reload start event)
|
|
||||||
ObjectsRemovalService::Flush();
|
|
||||||
|
|
||||||
// Give GC a try to cleanup old user objects and the other mess
|
|
||||||
MCore::GC::Collect();
|
|
||||||
MCore::GC::WaitForPendingFinalizers();
|
|
||||||
|
|
||||||
// Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload)
|
// Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload)
|
||||||
const auto flaxModule = GetBinaryModuleFlaxEngine();
|
ReleaseObjects(true);
|
||||||
_objectsLocker.Lock();
|
|
||||||
{
|
|
||||||
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
|
|
||||||
{
|
|
||||||
auto obj = i->Value;
|
|
||||||
if (obj->GetTypeHandle().Module == flaxModule)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
|
||||||
LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName));
|
|
||||||
#endif
|
|
||||||
obj->OnScriptingDispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_objectsLocker.Unlock();
|
|
||||||
|
|
||||||
// Release assets sourced from game assemblies
|
|
||||||
for (auto asset : Content::GetAssets())
|
|
||||||
{
|
|
||||||
if (asset->GetTypeHandle().Module == flaxModule)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
asset->DeleteObjectNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unload all game modules
|
// Unload all game modules
|
||||||
LOG(Info, "Unloading game binary modules");
|
LOG(Info, "Unloading game binary modules");
|
||||||
@@ -741,6 +721,15 @@ void Scripting::Reload(bool canTriggerSceneReload)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Array<ScriptingObject*, HeapAllocation> Scripting::GetObjects()
|
||||||
|
{
|
||||||
|
Array<ScriptingObject*> objects;
|
||||||
|
_objectsLocker.Lock();
|
||||||
|
_objectsDictionary.GetValues(objects);
|
||||||
|
_objectsLocker.Unlock();
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
MClass* Scripting::FindClass(const StringAnsiView& fullname)
|
MClass* Scripting::FindClass(const StringAnsiView& fullname)
|
||||||
{
|
{
|
||||||
if (fullname.IsEmpty())
|
if (fullname.IsEmpty())
|
||||||
@@ -860,7 +849,7 @@ void ScriptingObjectReferenceBase::OnDeleted(ScriptingObject* obj)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptingObject* Scripting::FindObject(Guid id, MClass* type)
|
ScriptingObject* Scripting::FindObject(Guid id, const MClass* type)
|
||||||
{
|
{
|
||||||
if (!id.IsValid())
|
if (!id.IsValid())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -914,7 +903,7 @@ ScriptingObject* Scripting::FindObject(Guid id, MClass* type)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type)
|
ScriptingObject* Scripting::TryFindObject(Guid id, const MClass* type)
|
||||||
{
|
{
|
||||||
if (!id.IsValid())
|
if (!id.IsValid())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -950,7 +939,7 @@ ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptingObject* Scripting::TryFindObject(MClass* type)
|
ScriptingObject* Scripting::TryFindObject(const MClass* type)
|
||||||
{
|
{
|
||||||
if (type == nullptr)
|
if (type == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -1020,7 +1009,7 @@ bool Scripting::IsEveryAssemblyLoaded()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Scripting::IsTypeFromGameScripts(MClass* type)
|
bool Scripting::IsTypeFromGameScripts(const MClass* type)
|
||||||
{
|
{
|
||||||
const auto binaryModule = ManagedBinaryModule::GetModule(type ? type->GetAssembly() : nullptr);
|
const auto binaryModule = ManagedBinaryModule::GetModule(type ? type->GetAssembly() : nullptr);
|
||||||
return binaryModule && binaryModule != GetBinaryModuleCorlib() && binaryModule != GetBinaryModuleFlaxEngine();
|
return binaryModule && binaryModule != GetBinaryModuleCorlib() && binaryModule != GetBinaryModuleFlaxEngine();
|
||||||
|
|||||||
@@ -79,6 +79,13 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all registered scripting objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Use with caution due to potentially large memory allocation.</remarks>
|
||||||
|
/// <returns>The collection of the objects.</returns>
|
||||||
|
static Array<ScriptingObject*, HeapAllocation> GetObjects();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the class with given fully qualified name within whole assembly.
|
/// Finds the class with given fully qualified name within whole assembly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -133,14 +140,14 @@ public:
|
|||||||
/// <param name="id">The object unique identifier.</param>
|
/// <param name="id">The object unique identifier.</param>
|
||||||
/// <param name="type">The type of the object to find (optional).</param>
|
/// <param name="type">The type of the object to find (optional).</param>
|
||||||
/// <returns>The found object or null if missing.</returns>
|
/// <returns>The found object or null if missing.</returns>
|
||||||
static ScriptingObject* FindObject(Guid id, MClass* type = nullptr);
|
static ScriptingObject* FindObject(Guid id, const MClass* type = nullptr);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find the object by the given class.
|
/// Tries to find the object by the given class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type of the object to find.</param>
|
/// <param name="type">The type of the object to find.</param>
|
||||||
/// <returns>The found object or null if missing.</returns>
|
/// <returns>The found object or null if missing.</returns>
|
||||||
static ScriptingObject* TryFindObject(MClass* type);
|
static ScriptingObject* TryFindObject(const MClass* type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find the object by the given identifier.
|
/// Tries to find the object by the given identifier.
|
||||||
@@ -159,7 +166,7 @@ public:
|
|||||||
/// <param name="id">The object unique identifier.</param>
|
/// <param name="id">The object unique identifier.</param>
|
||||||
/// <param name="type">The type of the object to find (optional).</param>
|
/// <param name="type">The type of the object to find (optional).</param>
|
||||||
/// <returns>The found object or null if missing.</returns>
|
/// <returns>The found object or null if missing.</returns>
|
||||||
static ScriptingObject* TryFindObject(Guid id, MClass* type = nullptr);
|
static ScriptingObject* TryFindObject(Guid id, const MClass* type = nullptr);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the object by the given managed instance handle. Searches only registered scene objects.
|
/// Finds the object by the given managed instance handle. Searches only registered scene objects.
|
||||||
@@ -189,7 +196,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if given type is from one of the game scripts assemblies.
|
/// Returns true if given type is from one of the game scripts assemblies.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static bool IsTypeFromGameScripts(MClass* type);
|
static bool IsTypeFromGameScripts(const MClass* type);
|
||||||
|
|
||||||
static void ProcessBuildInfoPath(String& path, const String& projectFolderPath);
|
static void ProcessBuildInfoPath(String& path, const String& projectFolderPath);
|
||||||
|
|
||||||
|
|||||||
@@ -1808,6 +1808,9 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Mark as modified (need to save texture data during scene saving)
|
||||||
|
_wasHeightModified = true;
|
||||||
|
|
||||||
if (!wasHeightChanged)
|
if (!wasHeightChanged)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -1820,9 +1823,6 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod
|
|||||||
#endif
|
#endif
|
||||||
_collisionVertices.Resize(0);
|
_collisionVertices.Resize(0);
|
||||||
|
|
||||||
// Mark as modified (need to save texture data during scene saving)
|
|
||||||
_wasHeightModified = true;
|
|
||||||
|
|
||||||
// Note: if terrain is using virtual storage then it won't be updated, we could synchronize that data...
|
// Note: if terrain is using virtual storage then it won't be updated, we could synchronize that data...
|
||||||
|
|
||||||
// TODO: disable heightmap dynamic streaming - data on a GPU was modified and we don't want to override it with the old data stored in the asset container
|
// TODO: disable heightmap dynamic streaming - data on a GPU was modified and we don't want to override it with the old data stored in the asset container
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include "Engine/Core/NonCopyable.h"
|
#include "Engine/Core/NonCopyable.h"
|
||||||
#include "Engine/Core/Enums.h"
|
#include "Engine/Core/Enums.h"
|
||||||
#include "Engine/Core/Types/TimeSpan.h"
|
#include "Engine/Core/Types/TimeSpan.h"
|
||||||
#include "Engine/Core/Collections/Array.h"
|
|
||||||
#include "Engine/Platform/Platform.h"
|
#include "Engine/Platform/Platform.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -188,8 +187,8 @@ public:
|
|||||||
/// <param name="tasks">The tasks list to wait for.</param>
|
/// <param name="tasks">The tasks list to wait for.</param>
|
||||||
/// <param name="timeoutMilliseconds">The maximum amount of milliseconds to wait for the task to finish it's job. Timeout smaller/equal 0 will result in infinite waiting.</param>
|
/// <param name="timeoutMilliseconds">The maximum amount of milliseconds to wait for the task to finish it's job. Timeout smaller/equal 0 will result in infinite waiting.</param>
|
||||||
/// <returns>True if any task failed or has been canceled or has timeout, otherwise false.</returns>
|
/// <returns>True if any task failed or has been canceled or has timeout, otherwise false.</returns>
|
||||||
template<class T = Task>
|
template<class T = Task, typename AllocationType = HeapAllocation>
|
||||||
static bool WaitAll(Array<T*>& tasks, double timeoutMilliseconds = -1)
|
static bool WaitAll(Array<T*, AllocationType>& tasks, double timeoutMilliseconds = -1)
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < tasks.Count(); i++)
|
for (int32 i = 0; i < tasks.Count(); i++)
|
||||||
{
|
{
|
||||||
@@ -300,27 +299,22 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cancels all the tasks from the list and clears it.
|
/// Cancels all the tasks from the list and clears it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
template<class T = Task>
|
template<class T = Task, typename AllocationType = HeapAllocation>
|
||||||
static void CancelAll(Array<T*>& tasks)
|
static void CancelAll(Array<T*, AllocationType>& tasks)
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < tasks.Count(); i++)
|
for (int32 i = 0; i < tasks.Count(); i++)
|
||||||
{
|
|
||||||
tasks[i]->Cancel();
|
tasks[i]->Cancel();
|
||||||
}
|
|
||||||
tasks.Clear();
|
tasks.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Executes this task.
|
/// Executes this task. It should be called by the task consumer (thread pool or other executor of this task type). It calls run() and handles result).
|
||||||
/// It should be called by the task consumer (thread pool or other executor of this task type).
|
|
||||||
/// It calls run() and handles result).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Execute();
|
void Execute();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs the task specified operations
|
/// Runs the task specified operations. It does not handle any task related logic, but only performs the actual job.
|
||||||
/// Does not handles any task related logic, only performs the actual job.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The task execution result. Returns true if failed, otherwise false.</returns>
|
/// <returns>The task execution result. Returns true if failed, otherwise false.</returns>
|
||||||
virtual bool Run() = 0;
|
virtual bool Run() = 0;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
#include "Engine/Core/Math/Math.h"
|
#include "Engine/Core/Math/Math.h"
|
||||||
#include "Engine/Core/Types/String.h"
|
#include "Engine/Core/Types/String.h"
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
#include "Engine/Engine/Globals.h"
|
#include "Engine/Engine/Globals.h"
|
||||||
#include "Engine/Engine/EngineService.h"
|
#include "Engine/Engine/EngineService.h"
|
||||||
#include "Engine/Platform/ConditionVariable.h"
|
#include "Engine/Platform/ConditionVariable.h"
|
||||||
|
|||||||
@@ -720,23 +720,23 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
|||||||
{
|
{
|
||||||
int vtxIndex = clusterIndices[j] - firstVertexOffset;
|
int vtxIndex = clusterIndices[j] - firstVertexOffset;
|
||||||
float vtxWeight = (float)clusterWeights[j];
|
float vtxWeight = (float)clusterWeights[j];
|
||||||
|
|
||||||
if (vtxWeight <= 0 || vtxIndex < 0 || vtxIndex >= vertexCount)
|
if (vtxWeight <= 0 || vtxIndex < 0 || vtxIndex >= vertexCount)
|
||||||
continue;
|
continue;
|
||||||
|
Int4& indices = mesh.BlendIndices.Get()[vtxIndex];
|
||||||
auto& indices = mesh.BlendIndices[vtxIndex];
|
Float4& weights = mesh.BlendWeights.Get()[vtxIndex];
|
||||||
auto& weights = mesh.BlendWeights[vtxIndex];
|
|
||||||
|
|
||||||
for (int32 k = 0; k < 4; k++)
|
for (int32 k = 0; k < 4; k++)
|
||||||
{
|
{
|
||||||
if (vtxWeight >= weights.Raw[k])
|
if (vtxWeight >= weights.Raw[k])
|
||||||
{
|
{
|
||||||
|
// Move lower weights by one down
|
||||||
for (int32 l = 2; l >= k; l--)
|
for (int32 l = 2; l >= k; l--)
|
||||||
{
|
{
|
||||||
indices.Raw[l + 1] = indices.Raw[l];
|
indices.Raw[l + 1] = indices.Raw[l];
|
||||||
weights.Raw[l + 1] = weights.Raw[l];
|
weights.Raw[l + 1] = weights.Raw[l];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set bone influence
|
||||||
indices.Raw[k] = boneIndex;
|
indices.Raw[k] = boneIndex;
|
||||||
weights.Raw[k] = vtxWeight;
|
weights.Raw[k] = vtxWeight;
|
||||||
break;
|
break;
|
||||||
@@ -786,11 +786,13 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
|||||||
auto shapeNormals = shape->getNormals();
|
auto shapeNormals = shape->getNormals();
|
||||||
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
||||||
{
|
{
|
||||||
/*auto delta = ToFloat3(shapeNormals[i + firstVertexOffset]) - mesh.Normals[i];
|
auto delta = ToFloat3(shapeNormals[i + firstVertexOffset]);
|
||||||
auto length = delta.Length();
|
if (data.ConvertRH)
|
||||||
if (length > ZeroTolerance)
|
{
|
||||||
delta /= length;*/
|
// Mirror normals along the Z axis
|
||||||
auto delta = Float3::Zero; // TODO: blend shape normals deltas fix when importing from fbx
|
delta.Z *= -1.0f;
|
||||||
|
}
|
||||||
|
delta = delta - mesh.Normals.Get()[i];
|
||||||
blendShapeData.Vertices.Get()[i].NormalDelta = delta;
|
blendShapeData.Vertices.Get()[i].NormalDelta = delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -800,7 +802,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
|||||||
{
|
{
|
||||||
// Mirror positions along the Z axis
|
// Mirror positions along the Z axis
|
||||||
for (int32 i = 0; i < vertexCount; i++)
|
for (int32 i = 0; i < vertexCount; i++)
|
||||||
mesh.Positions[i].Z *= -1.0f;
|
mesh.Positions.Get()[i].Z *= -1.0f;
|
||||||
for (auto& blendShapeData : mesh.BlendShapes)
|
for (auto& blendShapeData : mesh.BlendShapes)
|
||||||
{
|
{
|
||||||
for (auto& v : blendShapeData.Vertices)
|
for (auto& v : blendShapeData.Vertices)
|
||||||
@@ -815,7 +817,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
|||||||
{
|
{
|
||||||
// Invert the order
|
// Invert the order
|
||||||
for (int32 i = 0; i < mesh.Indices.Count(); i += 3)
|
for (int32 i = 0; i < mesh.Indices.Count(); i += 3)
|
||||||
Swap(mesh.Indices[i], mesh.Indices[i + 2]);
|
Swap(mesh.Indices.Get()[i], mesh.Indices.Get()[i + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((data.Options.CalculateTangents || !tangents) && mesh.UVs.HasItems())
|
if ((data.Options.CalculateTangents || !tangents) && mesh.UVs.HasItems())
|
||||||
|
|||||||
@@ -589,6 +589,9 @@ bool ModelTool::ImportData(const String& path, ModelData& data, Options& options
|
|||||||
{
|
{
|
||||||
for (auto& n : mesh->Normals)
|
for (auto& n : mesh->Normals)
|
||||||
n *= -1;
|
n *= -1;
|
||||||
|
for (auto& shape : mesh->BlendShapes)
|
||||||
|
for (auto& v : shape.Vertices)
|
||||||
|
v.NormalDelta *= -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ class GraphNode;
|
|||||||
|
|
||||||
#define GRAPH_NODE_MAKE_TYPE(groupID, typeID) (uint32)((groupID) << 16 | (typeID))
|
#define GRAPH_NODE_MAKE_TYPE(groupID, typeID) (uint32)((groupID) << 16 | (typeID))
|
||||||
|
|
||||||
#define GRAPH_NODE_MAX_VALUES 32
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents single box of the graph node
|
/// Represents single box of the graph node
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -114,14 +112,13 @@ public:
|
|||||||
uint16 TypeID;
|
uint16 TypeID;
|
||||||
uint16 GroupID;
|
uint16 GroupID;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32 Type;
|
uint32 Type;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of all node values. Array size and value types are constant over time. Only value data can change.
|
/// List of all node values. Array size and value types are constant over time. Only value data can change.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Array<Variant, FixedAllocation<GRAPH_NODE_MAX_VALUES>> Values;
|
Array<Variant, InlinedAllocation<8>> Values;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Node boxes cache. Array index matches the box ID (for fast O(1) lookups).
|
/// Node boxes cache. Array index matches the box ID (for fast O(1) lookups).
|
||||||
|
|||||||
@@ -6,13 +6,12 @@
|
|||||||
#include "Engine/Core/Math/Vector2.h"
|
#include "Engine/Core/Math/Vector2.h"
|
||||||
#include "Engine/Core/Math/Vector3.h"
|
#include "Engine/Core/Math/Vector3.h"
|
||||||
#include "Engine/Core/Math/Vector4.h"
|
#include "Engine/Core/Math/Vector4.h"
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
#include "Engine/Content/Asset.h"
|
#include "Engine/Content/Asset.h"
|
||||||
#include "Engine/Content/AssetReference.h"
|
#include "Engine/Content/AssetReference.h"
|
||||||
#include "Engine/Content/AssetsContainer.h"
|
#include "Engine/Content/AssetsContainer.h"
|
||||||
#include "Engine/Animations/Curve.h"
|
#include "Engine/Animations/Curve.h"
|
||||||
|
|
||||||
#define VISJECT_GRAPH_NODE_MAX_ASSETS 14
|
|
||||||
|
|
||||||
template<class BoxType>
|
template<class BoxType>
|
||||||
class VisjectGraphNode;
|
class VisjectGraphNode;
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ public:
|
|||||||
/// <summary>
|
/// <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.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AssetReference<Asset> Assets[VISJECT_GRAPH_NODE_MAX_ASSETS];
|
Array<AssetReference<Asset>> Assets;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -188,11 +187,10 @@ public:
|
|||||||
#undef SETUP_CURVE
|
#undef SETUP_CURVE
|
||||||
// Get Gameplay Global
|
// Get Gameplay Global
|
||||||
case 16:
|
case 16:
|
||||||
{
|
n->Assets.Resize(1);
|
||||||
n->Assets[0] = ::LoadAsset((Guid)n->Values[0], Asset::TypeInitializer);
|
n->Assets[0] = ::LoadAsset((Guid)n->Values[0], Asset::TypeInitializer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
|
|||||||
BIN
Source/Platforms/DotNet/Newtonsoft.Json.dll
(Stored with Git LFS)
BIN
Source/Platforms/DotNet/Newtonsoft.Json.dll
(Stored with Git LFS)
Binary file not shown.
BIN
Source/Platforms/DotNet/Newtonsoft.Json.pdb
(Stored with Git LFS)
BIN
Source/Platforms/DotNet/Newtonsoft.Json.pdb
(Stored with Git LFS)
Binary file not shown.
@@ -487,14 +487,37 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
|
|||||||
int checksLimit = 100;
|
int checksLimit = 100;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
// Loop until finds a point on a random poly that is in the radius
|
||||||
const float s = frand();
|
const float s = frand();
|
||||||
const float t = frand();
|
const float t = frand();
|
||||||
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
|
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
|
||||||
}
|
}
|
||||||
while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr && checksLimit-- > 0);
|
while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr && checksLimit-- > 0);
|
||||||
if (checksLimit <= 0)
|
if (checksLimit <= 0)
|
||||||
return DT_FAILURE;
|
{
|
||||||
|
// Check nearby polygons
|
||||||
|
float halfExtents[3] = { maxRadius, maxRadius, maxRadius };
|
||||||
|
dtPolyRef polys[32];
|
||||||
|
int polyCount;
|
||||||
|
queryPolygons(centerPos, halfExtents, filter, polys, &polyCount, 32);
|
||||||
|
for (int i = 0; i < polyCount && checksLimit <= 0; i++)
|
||||||
|
{
|
||||||
|
checksLimit = 100;
|
||||||
|
randomPolyRef = polys[i];
|
||||||
|
m_nav->getTileAndPolyByRefUnsafe(randomPolyRef, &randomTile, &randomPoly);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Loop until finds a point on a random poly that is in the radius
|
||||||
|
const float s = frand();
|
||||||
|
const float t = frand();
|
||||||
|
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
|
||||||
|
}
|
||||||
|
while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr && checksLimit-- > 0);
|
||||||
|
}
|
||||||
|
if (checksLimit <= 0)
|
||||||
|
return DT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
closestPointOnPoly(randomPolyRef, pt, pt, NULL);
|
closestPointOnPoly(randomPolyRef, pt, pt, NULL);
|
||||||
|
|
||||||
dtVcopy(randomPt, pt);
|
dtVcopy(randomPt, pt);
|
||||||
|
|||||||
Reference in New Issue
Block a user