Compare commits
166 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5520f2777 | |||
| 4414398f09 | |||
| 5e5497ff18 | |||
| aaadf3065d | |||
| b134a5567d | |||
| 740a31b7a9 | |||
| 75e25bf1f7 | |||
| 279c80a9ae | |||
| 4dbfc01715 | |||
| 45706be3a6 | |||
| 723423a3fa | |||
| 42de657629 | |||
| 16f1e8a3e1 | |||
| 2c1b2d2b7c | |||
| 19cc33b200 | |||
| 3641e156ee | |||
| 4413af5d20 | |||
| 8c2e7bccaa | |||
| 3e7ac04a88 | |||
| 9bc9d95a22 | |||
| b6e36c0014 | |||
| b6692b4747 | |||
| 62c2e3b8f0 | |||
| d4774a2bfe | |||
| 705856da24 | |||
| ab9cc16529 | |||
| 203f03a597 | |||
| 4fbe210730 | |||
| 6eb431d12c | |||
| 890569ea3b | |||
| d8a1de64d1 | |||
| b92fbcb3bc | |||
| 515ee96a31 | |||
| f19977a956 | |||
| 96f628429c | |||
| 3ecbbcd0a0 | |||
| a01495cde1 | |||
| b7dc0dd004 | |||
| 32b15f90ab | |||
| e795a8b037 | |||
| e551eae30d | |||
| 5f02b4173a | |||
| 568a69081d | |||
| 4dcdd8b5f7 | |||
| 1072b90c5b | |||
| 6aacea99ab | |||
| 5e086809ae | |||
| 361e9a2929 | |||
| b25ee23b14 | |||
| 41ffc16b66 | |||
| 560cf65121 | |||
| 93e26afa05 | |||
| 7653fba381 | |||
| 71fe280464 | |||
| 0845866c4d | |||
| a3b5f4b789 | |||
| 4b6b24f4c5 | |||
| d8079367fd | |||
| 83b9e6e32a | |||
| f8f1c02338 | |||
| 285710360c | |||
| 695c212cf0 | |||
| 05278ca418 | |||
| e31ce787aa | |||
| d379b4c046 | |||
| 261faad93e | |||
| 16554fe742 | |||
| 9f983cff49 | |||
| e53ab10145 | |||
| 692a61c948 | |||
| c83b74c85d | |||
| 171fc276fb | |||
| 446c1edafc | |||
| 03b498546d | |||
| 794b007170 | |||
| 26c2b33fc0 | |||
| 1a87e5a2ca | |||
| daf3671233 | |||
| 5fd808af19 | |||
| ce67c3a98d | |||
| cd2f96e3c0 | |||
| 9ad4997691 | |||
| 37a02e3a7e | |||
| ed732a0189 | |||
| 00492a33a3 | |||
| 56d3b4f012 | |||
| 85b9d93e91 | |||
| 8584d51d9f | |||
| 8714163ee4 | |||
| 525c3a0f29 | |||
| 5acdff02cc | |||
| 0728637ce1 | |||
| 9c3c4fbf54 | |||
| d2508ad902 | |||
| 8a0cd7c30f | |||
| d729eb2b24 | |||
| 36ad821734 | |||
| b66b85d5f4 | |||
| 778b967c61 | |||
| 3f6dd92a68 | |||
| 80d7ac2581 | |||
| af91ce7f3f | |||
| 5f7293d0a1 | |||
| 5cdf1c5764 | |||
| e701cdbee5 | |||
| dc45f46ff4 | |||
| 634b05fc16 | |||
| db28afb591 | |||
| 84c1f6b5de | |||
| 53689063b9 | |||
| c59e872ef2 | |||
| 2b95f11b1f | |||
| fbfe77e386 | |||
| 60ccac31b5 | |||
| d42e315e55 | |||
| 9c2c02c1cf | |||
| 8144db8e13 | |||
| 4ac6a292f7 | |||
| 3e475398e7 | |||
| 81d4501868 | |||
| 1bc7455e09 | |||
| 1e77f3aa5a | |||
| e47e91c223 | |||
| 734f1ee4aa | |||
| 6d38590ad4 | |||
| 76becec094 | |||
| e7a6f39a72 | |||
| 98c5cc2d0f | |||
| c8ad3e3a51 | |||
| 5b25aeda32 | |||
| e5d700692f | |||
| 495378c94d | |||
| bf9701e132 | |||
| 14881494b8 | |||
| c914e33462 | |||
| 35f33b6746 | |||
| e137d31839 | |||
| 2e643347ec | |||
| fb1685fe81 | |||
| 1ddf9ab0e1 | |||
| 8af8d50de1 | |||
| e3093e0e09 | |||
| 02d5609f66 | |||
| b2f9da4113 | |||
| a83b589e12 | |||
| 7578e1dbe3 | |||
| ae79f3ef0b | |||
| f164626c41 | |||
| 354972fd9c | |||
| 082733cb97 | |||
| 97bd90d4be | |||
| e788be46af | |||
| afd56974ef | |||
| 84e04de23d | |||
| 07c0b4567e | |||
| e25448e10e | |||
| d12630d815 | |||
| fb0d70d555 | |||
| c5e1abb08c | |||
| 294b4c4006 | |||
| e1944bce96 | |||
| 369c19bd5d | |||
| 55383c3fa4 | |||
| 926a81c84b | |||
| d331e6b848 | |||
| eac553a992 |
@@ -9,7 +9,7 @@ jobs:
|
||||
|
||||
# Editor
|
||||
editor-mac:
|
||||
name: Editor (Mac, Development x64)
|
||||
name: Editor (Mac, Development ARM64)
|
||||
runs-on: "macos-latest"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@@ -30,11 +30,11 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Build
|
||||
run: |
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor
|
||||
|
||||
# Game
|
||||
game-mac:
|
||||
name: Game (Mac, Release x64)
|
||||
name: Game (Mac, Release ARM64)
|
||||
runs-on: "macos-latest"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@@ -55,4 +55,4 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Build
|
||||
run: |
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Mac -configuration=Release -buildtargets=FlaxGame
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Release -buildtargets=FlaxGame
|
||||
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
dotnet workload --info
|
||||
- name: Build
|
||||
run: |
|
||||
./PackageEditor.command -arch=x64 -platform=Mac -deployOutput=Output
|
||||
./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -194,7 +194,7 @@ jobs:
|
||||
dotnet workload --info
|
||||
- name: Build
|
||||
run: |
|
||||
./PackagePlatforms.command -arch=x64 -platform=Mac -deployOutput=Output
|
||||
./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -430,7 +430,9 @@ float3x4 GetPrevBoneMatrix(int index)
|
||||
float3 SkinPrevPosition(ModelInput_Skinned input)
|
||||
{
|
||||
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.z * GetPrevBoneMatrix(input.BlendIndices.z);
|
||||
boneMatrix += input.BlendWeights.w * GetPrevBoneMatrix(input.BlendIndices.w);
|
||||
@@ -439,12 +441,6 @@ float3 SkinPrevPosition(ModelInput_Skinned input)
|
||||
|
||||
#endif
|
||||
|
||||
// Cached skinning data to avoid multiple calculation
|
||||
struct SkinningData
|
||||
{
|
||||
float3x4 BlendMatrix;
|
||||
};
|
||||
|
||||
// Calculates the transposed transform matrix for the given bone index
|
||||
float3x4 GetBoneMatrix(int index)
|
||||
{
|
||||
@@ -457,7 +453,9 @@ float3x4 GetBoneMatrix(int index)
|
||||
// Calculates the transposed transform matrix for the given vertex (uses blending)
|
||||
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.z * GetBoneMatrix(input.BlendIndices.z);
|
||||
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
|
||||
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
|
||||
float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data)
|
||||
float3x3 SkinTangents(ModelInput_Skinned input, float3x4 boneMatrix)
|
||||
{
|
||||
// Unpack vertex tangent frame
|
||||
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;
|
||||
|
||||
// Apply skinning
|
||||
tangent = mul(data.BlendMatrix, float4(tangent, 0));
|
||||
normal = mul(data.BlendMatrix, float4(normal, 0));
|
||||
tangent = normalize(mul(boneMatrix, float4(tangent, 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);
|
||||
}
|
||||
|
||||
@@ -501,10 +499,9 @@ VertexOutput VS_Skinned(ModelInput_Skinned input)
|
||||
VertexOutput output;
|
||||
|
||||
// Perform skinning
|
||||
SkinningData data;
|
||||
data.BlendMatrix = GetBoneMatrix(input);
|
||||
float3 position = SkinPosition(input, data);
|
||||
float3x3 tangentToLocal = SkinTangents(input, data);
|
||||
float3x4 boneMatrix = GetBoneMatrix(input);
|
||||
float3 position = SkinPosition(input, boneMatrix);
|
||||
float3x3 tangentToLocal = SkinTangents(input, boneMatrix);
|
||||
|
||||
// Compute world space vertex position
|
||||
CalculateInstanceTransform(input);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
-2
@@ -3,8 +3,8 @@
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 8,
|
||||
"Revision": 0,
|
||||
"Build": 6510
|
||||
"Revision": 1,
|
||||
"Build": 6511
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -79,6 +79,9 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SDK/@EntryIndexedValue">SDK</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=VS/@EntryIndexedValue">VS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue"><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue"><Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=53eecf85_002Dd821_002D40e8_002Dac97_002Dfdb734542b84/@EntryIndexedValue"><Policy><Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue"><Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
@@ -220,6 +223,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_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_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002EFunctionReturnStyleSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002ENamespaceIndentationSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
|
||||
@@ -232,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_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: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: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>
|
||||
|
||||
@@ -133,6 +133,7 @@ namespace FlaxEditor.Content.Import
|
||||
FileTypes["dds"] = ImportTexture;
|
||||
FileTypes["hdr"] = ImportTexture;
|
||||
FileTypes["raw"] = ImportTexture;
|
||||
FileTypes["exr"] = ImportTexture;
|
||||
|
||||
// Models
|
||||
FileTypes["obj"] = ImportModel;
|
||||
|
||||
@@ -128,6 +128,11 @@ namespace FlaxEditor.Content.Import
|
||||
_settings.Settings.Type = TextureFormatType.HdrRGBA;
|
||||
_settings.Settings.Compress = false;
|
||||
}
|
||||
else if (extension == ".exr")
|
||||
{
|
||||
// HDR image
|
||||
_settings.Settings.Type = TextureFormatType.HdrRGBA;
|
||||
}
|
||||
else if (extension == ".hdr")
|
||||
{
|
||||
// HDR sky texture
|
||||
|
||||
@@ -249,6 +249,7 @@ namespace FlaxEditor.Content
|
||||
private ScriptMemberInfo[] _parameters;
|
||||
private ScriptMemberInfo[] _methods;
|
||||
private object[] _attributes;
|
||||
private List<Action<ScriptType>> _disposing;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Visual Script asset that contains this type.
|
||||
@@ -310,6 +311,13 @@ namespace FlaxEditor.Content
|
||||
|
||||
internal void Dispose()
|
||||
{
|
||||
if (_disposing != null)
|
||||
{
|
||||
foreach (var e in _disposing)
|
||||
e(new ScriptType(this));
|
||||
_disposing.Clear();
|
||||
_disposing = null;
|
||||
}
|
||||
if (_parameters != null)
|
||||
{
|
||||
OnAssetReloading(_asset);
|
||||
@@ -510,6 +518,14 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
return _methods;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void TrackLifetime(Action<ScriptType> disposing)
|
||||
{
|
||||
if (_disposing == null)
|
||||
_disposing = new List<Action<ScriptType>>();
|
||||
_disposing.Add(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
Task.Run(() =>
|
||||
{
|
||||
Editor.CookMeshCollision(assetItem.Path, CollisionDataType.TriangleMesh, model);
|
||||
Editor.CookMeshCollision(assetItem.Path, CollisionDataType.ConvexMesh, model);
|
||||
if (created != null)
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() => created(collisionData));
|
||||
});
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace FlaxEditor.Content
|
||||
// Cleanup it after usage
|
||||
Object.Destroy(actor, 20.0f);
|
||||
}
|
||||
else if (actor.Scene != null)
|
||||
else if (actor.HasScene)
|
||||
{
|
||||
// Create prefab with identity transform so the actor instance on a level will have it customized
|
||||
resetTransform = true;
|
||||
|
||||
@@ -203,16 +203,16 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
|
||||
switch (defaultOrienation)
|
||||
{
|
||||
case AndroidPlatformSettings::ScreenOrientation::Portrait:
|
||||
orientation = String("portrait");
|
||||
orientation = String("userPortrait");
|
||||
break;
|
||||
case AndroidPlatformSettings::ScreenOrientation::PortraitReverse:
|
||||
orientation = String("reversePortrait");
|
||||
case AndroidPlatformSettings::ScreenOrientation::Landscape:
|
||||
orientation = String("userLandscape");
|
||||
break;
|
||||
case AndroidPlatformSettings::ScreenOrientation::LandscapeRight:
|
||||
orientation = String("landscape");
|
||||
case AndroidPlatformSettings::ScreenOrientation::SensorPortrait:
|
||||
orientation = String("sensorPortrait");
|
||||
break;
|
||||
case AndroidPlatformSettings::ScreenOrientation::LandscapeLeft:
|
||||
orientation = String("reverseLandscape");
|
||||
case AndroidPlatformSettings::ScreenOrientation::SensorLandscape:
|
||||
orientation = String("sensorLandscape");
|
||||
break;
|
||||
case AndroidPlatformSettings::ScreenOrientation::AutoRotation:
|
||||
orientation = String("fullSensor");
|
||||
@@ -266,9 +266,33 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
|
||||
}
|
||||
}
|
||||
|
||||
String versionCode = platformSettings->VersionCode;
|
||||
if (versionCode.IsEmpty())
|
||||
{
|
||||
LOG(Error, "AndroidSettings: Invalid version code");
|
||||
return true;
|
||||
}
|
||||
|
||||
String minimumSdk = platformSettings->MinimumAPILevel;
|
||||
if (minimumSdk.IsEmpty())
|
||||
{
|
||||
LOG(Error, "AndroidSettings: Invalid minimum API level");
|
||||
return true;
|
||||
}
|
||||
|
||||
String targetSdk = platformSettings->TargetAPILevel;
|
||||
if (targetSdk.IsEmpty())
|
||||
{
|
||||
LOG(Error, "AndroidSettings: Invalid target API level");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Format project template files
|
||||
const String buildGradlePath = data.OriginalOutputPath / TEXT("app/build.gradle");
|
||||
EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${PackageName}"), packageName);
|
||||
EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${VersionCode}"), versionCode);
|
||||
EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${MinimumSdk}"), minimumSdk);
|
||||
EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${TargetSdk}"), targetSdk);
|
||||
EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${ProjectVersion}"), projectVersion);
|
||||
EditorUtilities::ReplaceInFile(buildGradlePath, TEXT("${PackageAbi}"), abi);
|
||||
const String manifestPath = data.OriginalOutputPath / TEXT("app/src/main/AndroidManifest.xml");
|
||||
|
||||
@@ -536,7 +536,7 @@ namespace FlaxEditor.CustomEditors
|
||||
/// </summary>
|
||||
public void ClearReferenceValueAll()
|
||||
{
|
||||
Values.ClearReferenceValue();
|
||||
Values?.ClearReferenceValue();
|
||||
|
||||
for (int i = 0; i < ChildrenEditors.Count; i++)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Actions;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
@@ -9,6 +10,8 @@ using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Tree;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
@@ -111,6 +114,38 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var actor = (Actor)Values[0];
|
||||
var scriptType = TypeUtils.GetType(actor.TypeName);
|
||||
var item = scriptType.ContentItem;
|
||||
if (Presenter.Owner is PropertiesWindow propertiesWindow)
|
||||
{
|
||||
var lockButton = cm.AddButton(propertiesWindow.LockObjects ? "Unlock" : "Lock");
|
||||
lockButton.ButtonClicked += button =>
|
||||
{
|
||||
propertiesWindow.LockObjects = !propertiesWindow.LockObjects;
|
||||
|
||||
// Reselect current selection
|
||||
if (!propertiesWindow.LockObjects && Editor.Instance.SceneEditing.SelectionCount > 0)
|
||||
{
|
||||
var cachedSelection = Editor.Instance.SceneEditing.Selection.ToArray();
|
||||
Editor.Instance.SceneEditing.Select(null);
|
||||
Editor.Instance.SceneEditing.Select(cachedSelection);
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (Presenter.Owner is PrefabWindow prefabWindow)
|
||||
{
|
||||
var lockButton = cm.AddButton(prefabWindow.LockSelectedObjects ? "Unlock" : "Lock");
|
||||
lockButton.ButtonClicked += button =>
|
||||
{
|
||||
prefabWindow.LockSelectedObjects = !prefabWindow.LockSelectedObjects;
|
||||
|
||||
// Reselect current selection
|
||||
if (!prefabWindow.LockSelectedObjects && prefabWindow.Selection.Count > 0)
|
||||
{
|
||||
var cachedSelection = prefabWindow.Selection.ToList();
|
||||
prefabWindow.Select(null);
|
||||
prefabWindow.Select(cachedSelection);
|
||||
}
|
||||
};
|
||||
}
|
||||
cm.AddButton("Copy ID", OnClickCopyId);
|
||||
cm.AddButton("Edit actor type", OnClickEditActorType).Enabled = item != null;
|
||||
var showButton = cm.AddButton("Show in content window", OnClickShowActorType);
|
||||
@@ -164,7 +199,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
//Presenter.BuildLayoutOnUpdate();
|
||||
|
||||
// Better way is to just update the reference value using the new default instance of the prefab, created after changes apply
|
||||
if (prefab && !prefab.WaitForLoaded())
|
||||
if (Values != null && prefab && !prefab.WaitForLoaded())
|
||||
{
|
||||
var actor = (Actor)Values[0];
|
||||
var prefabObjectId = actor.PrefabObjectID;
|
||||
@@ -256,7 +291,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
if (editor.ChildrenEditors.Count == 0 || (isRefEdited && editor is CollectionEditor))
|
||||
result = CreateDiffNode(editor);
|
||||
bool isScriptEditorWithRefValue = editor is ScriptsEditor && editor.Values.HasReferenceValue;
|
||||
bool isActorEditorInLevel = editor is ActorEditor && editor.Values[0] is Actor actor && actor.IsPrefabRoot && actor.Scene != null;
|
||||
bool isActorEditorInLevel = editor is ActorEditor && editor.Values[0] is Actor actor && actor.IsPrefabRoot && actor.HasScene;
|
||||
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
|
||||
{
|
||||
var childEditor = editor.ChildrenEditors[i];
|
||||
|
||||
@@ -45,6 +45,12 @@ public class ModelPrefabEditor : GenericEditor
|
||||
break;
|
||||
_prefabId = prefabObject.PrefabID;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The model was removed earlier
|
||||
_prefabId = Guid.Empty;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var button = layout.Button("Reimport", "Reimports the source asset as prefab.");
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return;
|
||||
}
|
||||
|
||||
EvaluateVisibleIf(itemLayout, item, GetLabelIndex(itemLayout, item));
|
||||
|
||||
// Add labels with a check box
|
||||
var label = new CheckablePropertyNameLabel(item.DisplayName);
|
||||
label.CheckBox.Tag = setting.Bit;
|
||||
|
||||
@@ -209,16 +209,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
if (result != DragDropEffect.None || _dragHandlers == null)
|
||||
return result;
|
||||
|
||||
return _dragHandlers.Effect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_dragHandlers.OnDragLeave();
|
||||
_dragHandlers?.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// </summary>
|
||||
private class EditTangentOptionBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Spline editor reference.
|
||||
/// </summary>
|
||||
public SplineEditor Editor;
|
||||
|
||||
/// <summary>
|
||||
/// Called when user set selected tangent mode.
|
||||
/// </summary>
|
||||
@@ -103,7 +108,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
SetKeyframeLinear(spline, index);
|
||||
|
||||
// change the selection to tangent parent (a spline point / keyframe)
|
||||
SetSelectSplinePointNode(spline, index);
|
||||
Editor.SetSelectSplinePointNode(spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +177,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
SetTangentSmoothIn(spline, index);
|
||||
SetSelectTangentIn(spline, index);
|
||||
Editor.SetSelectTangentIn(spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +191,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
public override void OnSetMode(Spline spline, int index)
|
||||
{
|
||||
SetTangentSmoothOut(spline, index);
|
||||
SetSelectTangentOut(spline, index);
|
||||
Editor.SetSelectTangentOut(spline, index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +286,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
_currentTangentMode = new FreeTangentMode();
|
||||
_currentTangentMode = new FreeTangentMode { Editor = this };
|
||||
if (Values.HasDifferentTypes || !(Values[0] is Spline spline))
|
||||
return;
|
||||
_selectedSpline = spline;
|
||||
@@ -471,7 +476,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (_currentTangentMode is LinearTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new LinearTangentMode();
|
||||
_currentTangentMode = new LinearTangentMode { Editor = this };
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
@@ -479,7 +484,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (_currentTangentMode is FreeTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new FreeTangentMode();
|
||||
_currentTangentMode = new FreeTangentMode { Editor = this };
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
@@ -487,7 +492,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (_currentTangentMode is AlignedTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new AlignedTangentMode();
|
||||
_currentTangentMode = new AlignedTangentMode { Editor = this };
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
@@ -495,7 +500,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (_currentTangentMode is SmoothInTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new SmoothInTangentMode();
|
||||
_currentTangentMode = new SmoothInTangentMode { Editor = this };
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
@@ -503,17 +508,34 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (_currentTangentMode is SmoothOutTangentMode)
|
||||
return;
|
||||
_currentTangentMode = new SmoothOutTangentMode();
|
||||
_currentTangentMode = new SmoothOutTangentMode { Editor = this };
|
||||
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
|
||||
}
|
||||
|
||||
private List<SceneGraphNode> GetSelection()
|
||||
{
|
||||
if (Presenter.Owner is Windows.Assets.PrefabWindow prefabWindow)
|
||||
return prefabWindow.Selection;
|
||||
return Editor.Instance.SceneEditing.Selection;
|
||||
}
|
||||
|
||||
private void Select(SceneGraphNode node)
|
||||
{
|
||||
if (Presenter.Owner is Windows.Assets.PrefabWindow prefabWindow)
|
||||
{
|
||||
prefabWindow.Select(node);
|
||||
return;
|
||||
}
|
||||
Editor.Instance.SceneEditing.Select(node);
|
||||
}
|
||||
|
||||
private void UpdateSelectedPoint()
|
||||
{
|
||||
// works only if select one spline
|
||||
if (_selectedSpline)
|
||||
{
|
||||
var currentSelected = Editor.Instance.SceneEditing.Selection[0];
|
||||
|
||||
var selection = GetSelection();
|
||||
var currentSelected = selection.Count != 0 ? selection[0] : null;
|
||||
if (currentSelected == _selectedPoint)
|
||||
return;
|
||||
if (currentSelected is SplineNode.SplinePointNode selectedPoint)
|
||||
@@ -540,15 +562,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
private void UpdateSelectedTangent()
|
||||
{
|
||||
// works only if select one spline
|
||||
if (_lastPointSelected == null || Editor.Instance.SceneEditing.SelectionCount != 1)
|
||||
var selection = GetSelection();
|
||||
if (_lastPointSelected == null || selection.Count != 1)
|
||||
{
|
||||
_selectedTangentIn = null;
|
||||
_selectedTangentOut = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var currentSelected = Editor.Instance.SceneEditing.Selection[0];
|
||||
|
||||
var currentSelected = selection[0];
|
||||
if (currentSelected is not SplineNode.SplinePointTangentNode selectedPoint)
|
||||
{
|
||||
_selectedTangentIn = null;
|
||||
@@ -568,10 +589,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_selectedTangentIn = selectedPoint;
|
||||
_selectedTangentOut = null;
|
||||
_currentTangentMode.OnSelectTangent(_selectedSpline, index);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentSelected.Transform == _selectedSpline.GetSplineTangent(index, false))
|
||||
{
|
||||
_selectedTangentOut = selectedPoint;
|
||||
@@ -833,7 +852,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return null;
|
||||
}
|
||||
|
||||
private static SplineNode.SplinePointTangentNode GetSplineTangentOutNode(Spline spline, int index)
|
||||
private SplineNode.SplinePointTangentNode GetSplineTangentOutNode(Spline spline, int index)
|
||||
{
|
||||
var point = GetSplinePointNode(spline, index);
|
||||
var tangentOut = spline.GetSplineTangent(index, false);
|
||||
@@ -851,19 +870,19 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void SetSelectSplinePointNode(Spline spline, int index)
|
||||
private void SetSelectSplinePointNode(Spline spline, int index)
|
||||
{
|
||||
Editor.Instance.SceneEditing.Select(GetSplinePointNode(spline, index));
|
||||
Select(GetSplinePointNode(spline, index));
|
||||
}
|
||||
|
||||
private static void SetSelectTangentIn(Spline spline, int index)
|
||||
private void SetSelectTangentIn(Spline spline, int index)
|
||||
{
|
||||
Editor.Instance.SceneEditing.Select(GetSplineTangentInNode(spline, index));
|
||||
Select(GetSplineTangentInNode(spline, index));
|
||||
}
|
||||
|
||||
private static void SetSelectTangentOut(Spline spline, int index)
|
||||
private void SetSelectTangentOut(Spline spline, int index)
|
||||
{
|
||||
Editor.Instance.SceneEditing.Select(GetSplineTangentOutNode(spline, index));
|
||||
Select(GetSplineTangentOutNode(spline, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,7 +585,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
if (result != DragDropEffect.None || _dragHandlers == null)
|
||||
return result;
|
||||
|
||||
return _dragHandlers.Effect;
|
||||
@@ -594,7 +594,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_dragHandlers.OnDragLeave();
|
||||
_dragHandlers?.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
@@ -582,55 +582,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the property for the given item.
|
||||
/// Evaluate the <see cref="VisibleIfAttribute"/> cache for a given property item.
|
||||
/// </summary>
|
||||
/// <param name="itemLayout">The item layout.</param>
|
||||
/// <param name="itemValues">The item values.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
protected virtual void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
|
||||
/// <param name="labelIndex">The label index.</param>
|
||||
protected virtual void EvaluateVisibleIf(LayoutElementsContainer itemLayout, ItemInfo item, int labelIndex)
|
||||
{
|
||||
int labelIndex = 0;
|
||||
if ((item.IsReadOnly || item.VisibleIfs.Length > 0) &&
|
||||
itemLayout.Children.Count > 0 &&
|
||||
itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement propertiesListElement)
|
||||
{
|
||||
labelIndex = propertiesListElement.Labels.Count;
|
||||
}
|
||||
|
||||
itemLayout.Property(item.DisplayName, itemValues, item.OverrideEditor, item.TooltipText);
|
||||
|
||||
if (item.IsReadOnly && itemLayout.Children.Count > 0)
|
||||
{
|
||||
PropertiesListElement list = null;
|
||||
int firstChildControlIndex = 0;
|
||||
bool disableSingle = true;
|
||||
var control = itemLayout.Children[itemLayout.Children.Count - 1];
|
||||
if (control is GroupElement group && group.Children.Count > 0)
|
||||
{
|
||||
list = group.Children[0] as PropertiesListElement;
|
||||
disableSingle = false; // Disable all nested editors
|
||||
}
|
||||
else if (control is PropertiesListElement list1)
|
||||
{
|
||||
list = list1;
|
||||
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
|
||||
}
|
||||
|
||||
if (list != null)
|
||||
{
|
||||
// Disable controls added to the editor
|
||||
var count = list.Properties.Children.Count;
|
||||
for (int j = firstChildControlIndex; j < count; j++)
|
||||
{
|
||||
var child = list.Properties.Children[j];
|
||||
if (disableSingle && child is PropertyNameLabel)
|
||||
break;
|
||||
|
||||
if (child != null)
|
||||
child.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.VisibleIfs.Length > 0 && itemLayout.Children.Count > 0)
|
||||
{
|
||||
PropertiesListElement list = null;
|
||||
@@ -669,6 +627,73 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the label index.
|
||||
/// </summary>
|
||||
/// <param name="itemLayout">The item layout.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>The label index.</returns>
|
||||
protected virtual int GetLabelIndex(LayoutElementsContainer itemLayout, ItemInfo item)
|
||||
{
|
||||
int labelIndex = 0;
|
||||
if ((item.IsReadOnly || item.VisibleIfs.Length > 0) &&
|
||||
itemLayout.Children.Count > 0 &&
|
||||
itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement propertiesListElement)
|
||||
{
|
||||
labelIndex = propertiesListElement.Labels.Count;
|
||||
}
|
||||
|
||||
return labelIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the property for the given item.
|
||||
/// </summary>
|
||||
/// <param name="itemLayout">The item layout.</param>
|
||||
/// <param name="itemValues">The item values.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
protected virtual void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
|
||||
{
|
||||
int labelIndex = GetLabelIndex(itemLayout, item);
|
||||
|
||||
itemLayout.Property(item.DisplayName, itemValues, item.OverrideEditor, item.TooltipText);
|
||||
|
||||
if (item.IsReadOnly && itemLayout.Children.Count > 0)
|
||||
{
|
||||
PropertiesListElement list = null;
|
||||
int firstChildControlIndex = 0;
|
||||
bool disableSingle = true;
|
||||
var control = itemLayout.Children[itemLayout.Children.Count - 1];
|
||||
if (control is GroupElement group && group.Children.Count > 0)
|
||||
{
|
||||
list = group.Children[0] as PropertiesListElement;
|
||||
disableSingle = false; // Disable all nested editors
|
||||
}
|
||||
else if (control is PropertiesListElement list1)
|
||||
{
|
||||
list = list1;
|
||||
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
|
||||
}
|
||||
|
||||
if (list != null)
|
||||
{
|
||||
// Disable controls added to the editor
|
||||
var count = list.Properties.Children.Count;
|
||||
for (int j = firstChildControlIndex; j < count; j++)
|
||||
{
|
||||
var child = list.Properties.Children[j];
|
||||
if (disableSingle && child is PropertyNameLabel)
|
||||
break;
|
||||
|
||||
if (child != null)
|
||||
child.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EvaluateVisibleIf(itemLayout, item, labelIndex);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override void Initialize(CustomEditorPresenter presenter, LayoutElementsContainer layout, ValueContainer values)
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_comboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
}
|
||||
|
||||
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor)
|
||||
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor)
|
||||
{
|
||||
var button = menu.AddButton("Set to null");
|
||||
button.Clicked += () => _comboBox.SelectedItem = null;
|
||||
@@ -106,7 +106,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_comboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
}
|
||||
|
||||
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor)
|
||||
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor)
|
||||
{
|
||||
var button = menu.AddButton("Set to null");
|
||||
button.Clicked += () => _comboBox.SelectedItem = null;
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace FlaxEditor.CustomEditors
|
||||
var theFirstType = TypeUtils.GetObjectType(this[0]);
|
||||
for (int i = 1; i < Count; i++)
|
||||
{
|
||||
if (theFirstType != TypeUtils.GetObjectType(this[1]))
|
||||
if (theFirstType != TypeUtils.GetObjectType(this[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -554,7 +554,7 @@ namespace FlaxEditor.GUI
|
||||
// Check if has selected item
|
||||
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
|
||||
float textScale = Height / DefaultHeight;
|
||||
|
||||
@@ -42,15 +42,14 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
|
||||
// Arrange controls
|
||||
Margin margin = _menu._itemsMargin;
|
||||
float y = margin.Top;
|
||||
float x = margin.Left;
|
||||
float y = 0;
|
||||
float width = Width - margin.Width;
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i] is ContextMenuItem item && item.Visible)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -300,7 +299,6 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
if (_panel.Children[i] is ContextMenuChildMenu menu && menu.Text == text)
|
||||
return menu;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -319,7 +317,6 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
Parent = _panel
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -396,10 +393,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
float height = _itemsAreaMargin.Height;
|
||||
int itemsLeft = MaximumItemsInViewCount;
|
||||
int overflowItemCount = 0;
|
||||
int itemsCount = 0;
|
||||
for (int i = 0; i < _panel.Children.Count; i++)
|
||||
{
|
||||
if (_panel.Children[i] is ContextMenuItem item && item.Visible)
|
||||
{
|
||||
itemsCount++;
|
||||
if (itemsLeft > 0)
|
||||
{
|
||||
height += item.Height + _itemsMargin.Height;
|
||||
@@ -412,6 +411,8 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
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);
|
||||
|
||||
// Move child arrows to accommodate scroll bar showing
|
||||
|
||||
@@ -29,6 +29,15 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
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 />
|
||||
public override void Draw()
|
||||
{
|
||||
@@ -58,14 +67,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
var parentContextMenu = ParentContextMenu;
|
||||
if (parentContextMenu == ContextMenu)
|
||||
return;
|
||||
|
||||
if (ContextMenu.IsOpened)
|
||||
return;
|
||||
|
||||
base.OnMouseEnter(location);
|
||||
|
||||
// Hide parent CM popups and set itself as child
|
||||
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
|
||||
ShowChild(parentContextMenu);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -78,8 +85,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
if (ContextMenu.IsOpened)
|
||||
return true;
|
||||
|
||||
// Hide parent CM popups and set itself as child
|
||||
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
|
||||
ShowChild(parentContextMenu);
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
var color = (_timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground).AlphaMultiplied(0.6f);
|
||||
var color = (_timeline.IsMovingPositionHandle ? style.SelectionBorder : style.Foreground).AlphaMultiplied(0.6f);
|
||||
Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1);
|
||||
var m2 = Matrix3x3.Translation2D(0, timeAxisHeaderOffset);
|
||||
Matrix3x3.Multiply(ref m1, ref m2, out var m3);
|
||||
@@ -61,7 +61,8 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
||||
Render2D.DrawText(style.FontSmall, labelText, style.Foreground, new Float2(2, -6));
|
||||
Render2D.PopTransform();
|
||||
|
||||
Render2D.FillRectangle(new Rectangle(Width * 0.5f, Height + timeAxisHeaderOffset, 1, _timeline.MediaPanel.Height - timeAxisHeaderOffset - timeAxisOverlap), _timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground.RGBMultiplied(0.8f));
|
||||
color = _timeline.IsMovingPositionHandle ? style.SelectionBorder : style.Foreground.RGBMultiplied(0.8f);
|
||||
Render2D.FillRectangle(new Rectangle(Width * 0.5f, Height + timeAxisHeaderOffset, 1, _timeline.MediaPanel.Height - timeAxisHeaderOffset - timeAxisOverlap), color);
|
||||
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
||||
var timeAxisOverlap = Timeline.HeaderTopAreaHeight * 0.5f;
|
||||
var timeAxisHeaderOffset = -_timeline.MediaBackground.ViewOffset.Y - timeAxisOverlap;
|
||||
|
||||
var moveColor = style.ProgressNormal;
|
||||
var moveColor = style.SelectionBorder;
|
||||
var thickness = 2.0f;
|
||||
var borderColor = _isMoving ? moveColor : (IsMouseOver && _canEdit ? Color.Yellow : style.BorderNormal);
|
||||
Render2D.FillRectangle(new Rectangle((Width - thickness) * 0.5f, timeAxisHeaderOffset, thickness, Height - timeAxisHeaderOffset), borderColor);
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
private Track _tack;
|
||||
private int _startFrame, _durationFrames;
|
||||
private Float2 _mouseLocation = Float2.Minimum;
|
||||
private bool _isMoving;
|
||||
internal bool _isMoving;
|
||||
private Float2 _startMoveLocation;
|
||||
private int _startMoveStartFrame;
|
||||
private int _startMoveDuration;
|
||||
@@ -347,7 +347,7 @@ namespace FlaxEditor.GUI.Timeline
|
||||
|
||||
var isMovingWholeMedia = _isMoving && !_startMoveRightEdge && !_startMoveLeftEdge;
|
||||
var borderHighlightColor = style.BorderHighlighted;
|
||||
var moveColor = style.ProgressNormal;
|
||||
var moveColor = style.SelectionBorder;
|
||||
var selectedColor = style.BackgroundSelected;
|
||||
var moveThickness = 2.0f;
|
||||
var borderColor = isMovingWholeMedia ? moveColor : (Timeline.SelectedMedia.Contains(this) ? selectedColor : (IsMouseOver ? borderHighlightColor : style.BorderNormal));
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -100,6 +99,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
if (Type == ScriptType.Null)
|
||||
{
|
||||
Editor.LogError("Missing anim event type " + _instanceTypeName);
|
||||
InitMissing();
|
||||
return;
|
||||
}
|
||||
Instance = (AnimEvent)Type.CreateInstance();
|
||||
@@ -126,20 +126,37 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
_isRegisteredForScriptsReload = true;
|
||||
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)
|
||||
{
|
||||
Type = ScriptType.Null;
|
||||
IsContinuous = false;
|
||||
CanDelete = true;
|
||||
CanSplit = false;
|
||||
CanResize = false;
|
||||
TooltipText = $"Missing Anim Event Type '{typeName}'";
|
||||
Instance = null;
|
||||
BackgroundColor = Color.Red;
|
||||
_instanceTypeName = typeName;
|
||||
_instanceData = data;
|
||||
InitMissing();
|
||||
}
|
||||
|
||||
internal void Load(BinaryReader stream)
|
||||
@@ -183,6 +200,49 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
base.OnDurationFramesChanged();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool ContainsPoint(ref Float2 location, bool precise = false)
|
||||
{
|
||||
if (Timeline.Zoom > 0.5f && !IsContinuous)
|
||||
{
|
||||
// Hit-test dot
|
||||
var size = Height - 2.0f;
|
||||
var rect = new Rectangle(new Float2(size * -0.5f) + Size * 0.5f, new Float2(size));
|
||||
return rect.Contains(ref location);
|
||||
}
|
||||
|
||||
return base.ContainsPoint(ref location, precise);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
if (Timeline.Zoom > 0.5f && !IsContinuous)
|
||||
{
|
||||
// Draw more visible dot for the event that maintains size even when zooming out
|
||||
var style = Style.Current;
|
||||
var icon = Editor.Instance.Icons.VisjectBoxClosed32;
|
||||
var size = Height - 2.0f;
|
||||
var rect = new Rectangle(new Float2(size * -0.5f) + Size * 0.5f, new Float2(size));
|
||||
var outline = Color.Black; // Shadow
|
||||
if (_isMoving)
|
||||
outline = style.SelectionBorder;
|
||||
else if (IsMouseOver)
|
||||
outline = style.BorderHighlighted;
|
||||
else if (Timeline.SelectedMedia.Contains(this))
|
||||
outline = style.BackgroundSelected;
|
||||
Render2D.DrawSprite(icon, rect.MakeExpanded(6.0f), outline);
|
||||
Render2D.DrawSprite(icon, rect, BackgroundColor);
|
||||
|
||||
DrawChildren();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default drawing
|
||||
base.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -38,7 +38,6 @@ namespace FlaxEditor.GUI.Tree
|
||||
private bool _isMouseDown;
|
||||
private float _mouseDownTime;
|
||||
private Float2 _mouseDownPos;
|
||||
private Color _cachedTextColor;
|
||||
|
||||
private DragItemPositioning _dragOverMode;
|
||||
private bool _isDragOverHeader;
|
||||
@@ -91,6 +90,11 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the node is collapsed in the hierarchy (is collapsed or any of its parents is collapsed).
|
||||
/// </summary>
|
||||
public bool IsCollapsedInHierarchy => IsCollapsed || (Parent is TreeNode parentNode && parentNode.IsCollapsedInHierarchy);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text margin.
|
||||
/// </summary>
|
||||
@@ -604,9 +608,6 @@ namespace FlaxEditor.GUI.Tree
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
// Cache text color
|
||||
_cachedTextColor = CacheTextColor();
|
||||
|
||||
// Drop/down animation
|
||||
if (_animationProgress < 1.0f)
|
||||
{
|
||||
@@ -676,7 +677,8 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
|
||||
// Draw text
|
||||
Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center);
|
||||
Color textColor = CacheTextColor();
|
||||
Render2D.DrawText(TextFont.GetFont(), _text, textRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
|
||||
// Draw drag and drop effect
|
||||
if (IsDragOver && _tree.DraggedOverNode == this)
|
||||
@@ -712,6 +714,71 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DrawChildren()
|
||||
{
|
||||
// Draw all visible child controls
|
||||
var children = _children;
|
||||
if (children.Count == 0)
|
||||
return;
|
||||
|
||||
if (CullChildren)
|
||||
{
|
||||
Render2D.PeekClip(out var globalClipping);
|
||||
Render2D.PeekTransform(out var globalTransform);
|
||||
|
||||
// Try to estimate the rough location of the first node, assuming the node height is constant
|
||||
var firstChildGlobalRect = GetChildGlobalRectangle(children[0], ref globalTransform);
|
||||
var firstVisibleChild = Math.Clamp((int)Math.Floor((globalClipping.Y - firstChildGlobalRect.Top) / firstChildGlobalRect.Height) + 1, 0, children.Count - 1);
|
||||
if (GetChildGlobalRectangle(children[firstVisibleChild], ref globalTransform).Top > globalClipping.Top)
|
||||
{
|
||||
// Overshoot...
|
||||
for (; firstVisibleChild > 0; firstVisibleChild--)
|
||||
{
|
||||
var child = children[firstVisibleChild];
|
||||
if (GetChildGlobalRectangle(child, ref globalTransform).Top < globalClipping.Top)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = firstVisibleChild; i < children.Count; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
if (child.Visible)
|
||||
{
|
||||
var childGlobalRect = GetChildGlobalRectangle(child, ref globalTransform);
|
||||
if (globalClipping.Intersects(ref childGlobalRect))
|
||||
{
|
||||
Render2D.PushTransform(ref child._cachedTransform);
|
||||
child.Draw();
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Rectangle GetChildGlobalRectangle(Control control, ref Matrix3x3 globalTransform)
|
||||
{
|
||||
Matrix3x3.Multiply(ref control._cachedTransform, ref globalTransform, out var globalChildTransform);
|
||||
return new Rectangle(globalChildTransform.M31, globalChildTransform.M32, control.Width * globalChildTransform.M11, control.Height * globalChildTransform.M22);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
if (child.Visible)
|
||||
{
|
||||
Render2D.PushTransform(ref child._cachedTransform);
|
||||
child.Draw();
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
@@ -996,7 +1063,7 @@ namespace FlaxEditor.GUI.Tree
|
||||
// Expand node if mouse goes over arrow
|
||||
if (ArrowRect.Contains(location) && HasAnyVisibleChild)
|
||||
Expand(true);
|
||||
|
||||
|
||||
if (!_isDragOverHeader)
|
||||
result = OnDragEnterHeader(data);
|
||||
else
|
||||
@@ -1104,7 +1171,6 @@ namespace FlaxEditor.GUI.Tree
|
||||
{
|
||||
// TODO: perform layout for any non-TreeNode controls
|
||||
_cachedHeight = _headerHeight;
|
||||
_cachedTextColor = CacheTextColor();
|
||||
Size = new Float2(width, _headerHeight);
|
||||
}
|
||||
|
||||
@@ -1154,7 +1220,6 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
|
||||
_cachedHeight = height;
|
||||
_cachedTextColor = CacheTextColor();
|
||||
Height = Mathf.Max(_headerHeight, y);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Renderer/ProbesRenderer.h"
|
||||
#include "Engine/Animations/Graph/AnimGraph.h"
|
||||
#include "Engine/Core/ObjectsRemovalService.h"
|
||||
|
||||
ManagedEditor::InternalOptions ManagedEditor::ManagedEditorOptions;
|
||||
|
||||
@@ -572,6 +573,29 @@ bool ManagedEditor::EvaluateVisualScriptLocal(VisualScript* script, VisualScript
|
||||
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)
|
||||
{
|
||||
ASSERT(!HasManagedInstance());
|
||||
|
||||
@@ -241,6 +241,7 @@ public:
|
||||
API_FUNCTION(Internal) static VisualScriptStackFrame GetVisualScriptPreviousScopeFrame();
|
||||
API_FUNCTION(Internal) static Array<VisualScriptLocal> GetVisualScriptLocals();
|
||||
API_FUNCTION(Internal) static bool EvaluateVisualScriptLocal(VisualScript* script, API_PARAM(Ref) VisualScriptLocal& local);
|
||||
API_FUNCTION(Internal) static void WipeOutLeftoverSceneObjects();
|
||||
|
||||
private:
|
||||
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
// When applying changes to prefab from actor in level ignore it's root transformation (see ActorEditor.ProcessDiff)
|
||||
var originalTransform = instance.LocalTransform;
|
||||
if (instance.IsPrefabRoot && instance.Scene != null)
|
||||
if (instance.IsPrefabRoot && instance.HasScene)
|
||||
instance.LocalTransform = prefab.GetDefaultInstance().Transform;
|
||||
|
||||
// Call backend
|
||||
|
||||
@@ -717,19 +717,6 @@ namespace FlaxEditor.Modules
|
||||
_toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip($"Change Gizmo tool mode to Scale ({inputOptions.ScaleMode})");
|
||||
ToolStrip.AddSeparator();
|
||||
|
||||
// Build scenes
|
||||
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip($"Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options ({inputOptions.BuildScenesData})");
|
||||
|
||||
// Cook and run
|
||||
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.CookAndRun})");
|
||||
_toolStripCook.ContextMenu = new ContextMenu();
|
||||
_toolStripCook.ContextMenu.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked);
|
||||
_toolStripCook.ContextMenu.AddSeparator();
|
||||
var numberOfClientsMenu = _toolStripCook.ContextMenu.AddChildMenu("Number of game clients");
|
||||
_numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu);
|
||||
|
||||
ToolStrip.AddSeparator();
|
||||
|
||||
// Play
|
||||
_toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, Editor.Simulation.DelegatePlayOrStopPlayInEditor).LinkTooltip($"Play In Editor ({inputOptions.Play})");
|
||||
_toolStripPlay.ContextMenu = new ContextMenu();
|
||||
@@ -741,7 +728,6 @@ namespace FlaxEditor.Modules
|
||||
playActionGroup.Selected = Editor.Options.Options.Interface.PlayButtonAction;
|
||||
playActionGroup.SelectedChanged = SetPlayAction;
|
||||
Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; };
|
||||
|
||||
var windowModesGroup = new ContextMenuSingleSelectGroup<InterfaceOptions.GameWindowMode>();
|
||||
var windowTypeMenu = _toolStripPlay.ContextMenu.AddChildMenu("Game window mode");
|
||||
windowModesGroup.AddItem("Docked", InterfaceOptions.GameWindowMode.Docked, null, "Shows the game window docked, inside the editor");
|
||||
@@ -754,7 +740,20 @@ namespace FlaxEditor.Modules
|
||||
Editor.Options.OptionsChanged += options => { windowModesGroup.Selected = options.Interface.DefaultGameWindowMode; };
|
||||
|
||||
_toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game ({inputOptions.Pause})");
|
||||
_toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game");
|
||||
_toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip($"Step one frame in game ({inputOptions.StepFrame})");
|
||||
|
||||
ToolStrip.AddSeparator();
|
||||
|
||||
// Build scenes
|
||||
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip($"Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options ({inputOptions.BuildScenesData})");
|
||||
|
||||
// Cook and run
|
||||
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip($"Cook & Run - build game for the current platform and run it locally ({inputOptions.CookAndRun})");
|
||||
_toolStripCook.ContextMenu = new ContextMenu();
|
||||
_toolStripCook.ContextMenu.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked);
|
||||
_toolStripCook.ContextMenu.AddSeparator();
|
||||
var numberOfClientsMenu = _toolStripCook.ContextMenu.AddChildMenu("Number of game clients");
|
||||
_numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu);
|
||||
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
@@ -320,6 +320,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Viewport"), EditorOrder(1750)]
|
||||
public InputBinding ViewpointBottom = new InputBinding(KeyboardKeys.Numpad2);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "NumpadDecimal")]
|
||||
[EditorDisplay("Viewport"), EditorOrder(1760)]
|
||||
public InputBinding ToggleOrthographic = new InputBinding(KeyboardKeys.NumpadDecimal);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Interface
|
||||
|
||||
@@ -49,12 +49,14 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
get
|
||||
{
|
||||
var actor = (Spline)_node.Actor;
|
||||
return actor.GetSplineTransform(Index);
|
||||
var actor = (Spline)_node?.Actor;
|
||||
return actor?.GetSplineTransform(Index) ?? Transform.Identity;
|
||||
}
|
||||
set
|
||||
{
|
||||
var actor = (Spline)_node.Actor;
|
||||
var actor = (Spline)_node?.Actor;
|
||||
if (actor == null)
|
||||
return;
|
||||
actor.SetSplineTransform(Index, value);
|
||||
OnSplineEdited(actor);
|
||||
}
|
||||
|
||||
@@ -91,15 +91,11 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
private void OnAddMeshCollider(EditorWindow window)
|
||||
{
|
||||
// Allow collider to be added to evey static model selection
|
||||
SceneGraphNode[] selection = Array.Empty<SceneGraphNode>();
|
||||
var selection = Array.Empty<SceneGraphNode>();
|
||||
if (window is SceneTreeWindow)
|
||||
{
|
||||
selection = Editor.Instance.SceneEditing.Selection.ToArray();
|
||||
}
|
||||
else if (window is PrefabWindow prefabWindow)
|
||||
{
|
||||
selection = prefabWindow.Selection.ToArray();
|
||||
}
|
||||
|
||||
var createdNodes = new List<SceneGraphNode>();
|
||||
foreach (var node in selection)
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
Visible = (actor.HideFlags & HideFlags.HideInHierarchy) == 0;
|
||||
|
||||
// Pick the correct id when inside a prefab window.
|
||||
var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
|
||||
var id = actor.HasPrefabLink && !actor.HasScene ? actor.PrefabObjectID : actor.ID;
|
||||
if (Editor.Instance.ProjectCache.IsExpandedActor(ref id))
|
||||
{
|
||||
Expand(true);
|
||||
@@ -97,7 +97,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
parentTreeNode.IsLayoutLocked = false;
|
||||
|
||||
// Skip UI update if node won't be in a view
|
||||
if (parentTreeNode.IsCollapsed)
|
||||
if (parentTreeNode.IsCollapsedInHierarchy)
|
||||
{
|
||||
UnlockChildrenRecursive();
|
||||
}
|
||||
@@ -291,7 +291,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
return Style.Current.ForegroundGrey;
|
||||
}
|
||||
|
||||
if (actor.Scene != null && Editor.Instance.StateMachine.IsPlayMode && actor.IsStatic)
|
||||
if (actor.HasScene && Editor.Instance.StateMachine.IsPlayMode && actor.IsStatic)
|
||||
{
|
||||
// Static
|
||||
return color * 0.85f;
|
||||
@@ -366,7 +366,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
if (!IsLayoutLocked && actor)
|
||||
{
|
||||
// Pick the correct id when inside a prefab window.
|
||||
var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
|
||||
var id = actor.HasPrefabLink && !actor.HasScene ? actor.PrefabObjectID : actor.ID;
|
||||
Editor.Instance.ProjectCache.SetExpandedActor(ref id, IsExpanded);
|
||||
}
|
||||
}
|
||||
@@ -640,8 +640,8 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
private bool ValidateDragScript(Script script)
|
||||
{
|
||||
// Reject dragging scripts not linked to scene (eg. from prefab) or in the opposite way
|
||||
var thisHasScene = Actor.Scene != null;
|
||||
var otherHasScene = script.Scene != null;
|
||||
var thisHasScene = Actor.HasScene;
|
||||
var otherHasScene = script.HasScene;
|
||||
if (thisHasScene != otherHasScene)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace FlaxEditor.SceneGraph
|
||||
private void OnActorSpawned(Actor actor)
|
||||
{
|
||||
// Skip actors from game
|
||||
if (actor.Scene != null)
|
||||
if (actor.HasScene)
|
||||
return;
|
||||
|
||||
// Check if it has parent
|
||||
|
||||
@@ -144,5 +144,11 @@ namespace FlaxEditor.Scripting
|
||||
{
|
||||
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>
|
||||
/// <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);
|
||||
|
||||
/// <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>
|
||||
|
||||
@@ -1395,11 +1395,21 @@ namespace FlaxEditor.Scripting
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <param name="from">Source 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)
|
||||
{
|
||||
if (from == to)
|
||||
@@ -1412,10 +1422,10 @@ namespace FlaxEditor.Scripting
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <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)
|
||||
{
|
||||
return CanCast(this, to);
|
||||
|
||||
@@ -163,6 +163,8 @@ namespace FlaxEditor.States
|
||||
Editor.OnPlayBegin();
|
||||
IsPlayModeStarting = false;
|
||||
Profiler.EndEvent();
|
||||
|
||||
Time.Synchronize();
|
||||
}
|
||||
|
||||
private void SetupEditorEnvOptions()
|
||||
@@ -192,7 +194,7 @@ namespace FlaxEditor.States
|
||||
|
||||
// Restore editor scene
|
||||
SceneRestoring?.Invoke();
|
||||
_duplicateScenes.DeletedScenes();
|
||||
_duplicateScenes.UnloadScenes();
|
||||
PluginManager.Internal_DeinitializeGamePlugins();
|
||||
Editor.Internal_SetPlayMode(false);
|
||||
_duplicateScenes.RestoreSceneData();
|
||||
@@ -209,6 +211,8 @@ namespace FlaxEditor.States
|
||||
Editor.OnPlayEnd();
|
||||
IsPlayModeEnding = false;
|
||||
Profiler.EndEvent();
|
||||
|
||||
Time.Synchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.IO;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Undo;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
@@ -16,14 +17,13 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
[HideInEditor]
|
||||
public abstract class BlendPointsEditor : ContainerControl
|
||||
public class BlendPointsEditor : ContainerControl
|
||||
{
|
||||
private readonly Animation.MultiBlend _node;
|
||||
private readonly bool _is2D;
|
||||
private Float2 _rangeX;
|
||||
private Float2 _rangeY;
|
||||
private readonly BlendPoint[] _blendPoints = new BlendPoint[Animation.MultiBlend.MaxAnimationsCount];
|
||||
private readonly Guid[] _pointsAnims = new Guid[Animation.MultiBlend.MaxAnimationsCount];
|
||||
private readonly Float2[] _pointsLocations = new Float2[Animation.MultiBlend.MaxAnimationsCount];
|
||||
private Float2 _rangeX, _rangeY;
|
||||
private Float2 _debugPos = Float2.Minimum;
|
||||
private readonly List<BlendPoint> _blendPoints = new List<BlendPoint>();
|
||||
|
||||
/// <summary>
|
||||
/// Represents single blend point.
|
||||
@@ -31,16 +31,22 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <seealso cref="FlaxEngine.GUI.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 int _index;
|
||||
private bool _isMouseDown;
|
||||
private Float2 _mousePosOffset;
|
||||
private bool _isMouseDown, _mouseMoved;
|
||||
private object[] _mouseMoveStartValues;
|
||||
|
||||
/// <summary>
|
||||
/// The default size for the blend points.
|
||||
/// </summary>
|
||||
public const float DefaultSize = 8.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Blend point index.
|
||||
/// </summary>
|
||||
public int Index => _index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
||||
/// </summary>
|
||||
@@ -53,24 +59,45 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_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 />
|
||||
public override void OnGotFocus()
|
||||
{
|
||||
base.OnGotFocus();
|
||||
|
||||
_editor.SelectedIndex = _index;
|
||||
_editor._node.SelectedAnimationIndex = _index;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
// Cache data
|
||||
var isSelected = _editor.SelectedIndex == _index;
|
||||
|
||||
// Draw rotated rectangle
|
||||
Render2D.PushTransform(ref _transform);
|
||||
Render2D.FillRectangle(new Rectangle(0, 0, 5, 5), isSelected ? Color.Orange : Color.BlueViolet);
|
||||
Render2D.PopTransform();
|
||||
// Draw dot with outline
|
||||
var style = Style.Current;
|
||||
var icon = Editor.Instance.Icons.VisjectBoxClosed32;
|
||||
var size = Height;
|
||||
var rect = new Rectangle(new Float2(size * -0.5f) + Size * 0.5f, new Float2(size));
|
||||
var outline = Color.Black; // Shadow
|
||||
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 />
|
||||
@@ -80,6 +107,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
Focus();
|
||||
_isMouseDown = true;
|
||||
_mouseMoved = false;
|
||||
_mouseMoveStartValues = null;
|
||||
_mousePosOffset = -location;
|
||||
StartMouseCapture();
|
||||
return true;
|
||||
}
|
||||
@@ -92,8 +122,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
if (button == MouseButton.Left && _isMouseDown)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
EndMouseCapture();
|
||||
EndMove();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -103,9 +132,26 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
@@ -116,8 +162,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
if (_isMouseDown)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
EndMouseCapture();
|
||||
EndMove();
|
||||
}
|
||||
|
||||
base.OnMouseLeave();
|
||||
@@ -128,8 +173,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
if (_isMouseDown)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
EndMouseCapture();
|
||||
EndMove();
|
||||
}
|
||||
|
||||
base.OnLostFocus();
|
||||
@@ -149,40 +193,130 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
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>
|
||||
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
||||
/// </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="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 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)
|
||||
{
|
||||
_node = node;
|
||||
_is2D = is2D;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blend space data.
|
||||
/// </summary>
|
||||
/// <param name="rangeX">The space range for X axis (X-width, Y-height).</param>
|
||||
/// <param name="rangeY">The space range for Y axis (X-width, Y-height).</param>
|
||||
/// <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);
|
||||
internal void AddPoint()
|
||||
{
|
||||
// Add random point within range
|
||||
var rand = new Float2(Mathf.Frac((float)Platform.TimeSeconds), (Platform.TimeCycles % 10000) / 10000.0f);
|
||||
AddPoint(Float2.Lerp(new Float2(_rangeX.X, _rangeY.X), new Float2(_rangeX.Y, _rangeY.Y), rand));
|
||||
}
|
||||
|
||||
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>
|
||||
/// Gets or sets the index of the selected blend point.
|
||||
/// Sets the blend point asset.
|
||||
/// </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>
|
||||
/// Sets the blend point location.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</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>
|
||||
/// Gets the blend points area.
|
||||
@@ -249,10 +383,23 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
// Synchronize blend points collection
|
||||
GetData(out _rangeX, out _rangeY, _pointsAnims, _pointsLocations);
|
||||
for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++)
|
||||
var data0 = (Float4)_node.Values[0];
|
||||
_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)
|
||||
{
|
||||
@@ -264,7 +411,13 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 />
|
||||
public override void Draw()
|
||||
{
|
||||
// Cache data
|
||||
var style = Style.Current;
|
||||
var rect = new Rectangle(Float2.Zero, Size);
|
||||
var containsFocus = ContainsFocus;
|
||||
GetPointsArea(out var pointsArea);
|
||||
var data0 = (Float4)_node.Values[0];
|
||||
var rangeX = new Float2(data0.X, data0.Y);
|
||||
|
||||
// Background
|
||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||
@@ -309,19 +558,27 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
// Grid
|
||||
int splits = 10;
|
||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||
var labelColor = style.ForegroundDisabled;
|
||||
var labelFont = style.FontSmall;
|
||||
//var blendArea = BlendAreaRect;
|
||||
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;
|
||||
Render2D.DrawLine(new Float2(x, 1), new Float2(x, rect.Height - 2), gridColor);
|
||||
float alpha = (float)i / splits;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||
float alpha = (float)i / splits;
|
||||
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
|
||||
@@ -330,11 +587,24 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||
}
|
||||
|
||||
// Base
|
||||
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
|
||||
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" />
|
||||
public abstract class MultiBlend : SurfaceNode
|
||||
{
|
||||
private Button _addButton;
|
||||
private Button _removeButton;
|
||||
|
||||
/// <summary>
|
||||
/// The blend space editor.
|
||||
/// </summary>
|
||||
protected BlendPointsEditor _editor;
|
||||
|
||||
/// <summary>
|
||||
/// The selected animation label.
|
||||
/// </summary>
|
||||
@@ -379,7 +657,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <summary>
|
||||
/// The maximum animations amount to blend per node.
|
||||
/// </summary>
|
||||
public const int MaxAnimationsCount = 14;
|
||||
public const int MaxAnimationsCount = 255;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the selected animation.
|
||||
@@ -387,7 +665,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
public int SelectedAnimationIndex
|
||||
{
|
||||
get => _selectedAnimation.SelectedIndex;
|
||||
set => _selectedAnimation.SelectedIndex = value;
|
||||
set
|
||||
{
|
||||
OnSelectedAnimationPopupShowing(_selectedAnimation);
|
||||
_selectedAnimation.SelectedIndex = value;
|
||||
UpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -402,20 +685,14 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Text = "Selected Animation:",
|
||||
Parent = this
|
||||
};
|
||||
|
||||
_selectedAnimation = new ComboBox(_selectedAnimationLabel.X, 4 * layoutOffsetY, _selectedAnimationLabel.Width)
|
||||
{
|
||||
TooltipText = "Select blend point to view and edit it",
|
||||
Parent = this
|
||||
};
|
||||
|
||||
_selectedAnimation.PopupShowing += OnSelectedAnimationPopupShowing;
|
||||
_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))
|
||||
{
|
||||
Parent = this
|
||||
@@ -428,20 +705,36 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Text = "Speed:",
|
||||
Parent = this
|
||||
};
|
||||
|
||||
_animationSpeed = new FloatValueBox(1.0f, _animationSpeedLabel.Right + 4, _animationSpeedLabel.Y, _selectedAnimation.Right - _animationSpeedLabel.Right - 4)
|
||||
{
|
||||
SlideSpeed = 0.01f,
|
||||
Parent = this
|
||||
};
|
||||
_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)
|
||||
{
|
||||
var items = comboBox.Items;
|
||||
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 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>
|
||||
/// Updates the editor UI.
|
||||
/// </summary>
|
||||
@@ -491,7 +794,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <param name="isValid">if set to <c>true</c> is selection valid.</param>
|
||||
/// <param name="data0">The packed data 0.</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)
|
||||
{
|
||||
@@ -511,19 +814,21 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_animationPicker.Enabled = isValid;
|
||||
_animationSpeedLabel.Enabled = isValid;
|
||||
_animationSpeed.Enabled = isValid;
|
||||
_addButton.Enabled = _editor.PointsCount < MaxAnimationsCount;
|
||||
_removeButton.Enabled = isValid && data1 != Guid.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the editor UI.
|
||||
/// </summary>
|
||||
protected void UpdateUI()
|
||||
public void UpdateUI()
|
||||
{
|
||||
if (_isUpdatingUI)
|
||||
return;
|
||||
_isUpdatingUI = true;
|
||||
|
||||
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||
var isValid = selectedIndex != -1;
|
||||
var isValid = selectedIndex >= 0 && selectedIndex < _editor.PointsCount;
|
||||
Float4 data0;
|
||||
Guid data1;
|
||||
if (isValid)
|
||||
@@ -549,6 +854,16 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
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 />
|
||||
public override void OnValuesChanged()
|
||||
{
|
||||
@@ -566,67 +881,6 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
private readonly Label _animationXLabel;
|
||||
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 />
|
||||
public MultiBlend1D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
@@ -646,11 +900,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
};
|
||||
_animationX.ValueChanged += OnAnimationXChanged;
|
||||
|
||||
_editor = new Editor(this,
|
||||
FlaxEditor.Surface.Constants.NodeMarginX,
|
||||
_animationX.Bottom + 4.0f,
|
||||
Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f,
|
||||
120.0f);
|
||||
var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f;
|
||||
_editor = new BlendPointsEditor(this, false, FlaxEditor.Surface.Constants.NodeMarginX, _animationX.Bottom + 4.0f, size, 120.0f);
|
||||
_editor.Parent = this;
|
||||
}
|
||||
|
||||
@@ -670,7 +921,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
@@ -700,68 +951,6 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
private readonly FloatValueBox _animationX;
|
||||
private readonly Label _animationYLabel;
|
||||
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 />
|
||||
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
@@ -795,11 +984,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
};
|
||||
_animationY.ValueChanged += OnAnimationYChanged;
|
||||
|
||||
_editor = new Editor(this,
|
||||
FlaxEditor.Surface.Constants.NodeMarginX,
|
||||
_animationY.Bottom + 4.0f,
|
||||
Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f,
|
||||
120.0f);
|
||||
var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f;
|
||||
_editor = new BlendPointsEditor(this, true, FlaxEditor.Surface.Constants.NodeMarginX, _animationY.Bottom + 4.0f, size, size);
|
||||
_editor.Parent = this;
|
||||
}
|
||||
|
||||
@@ -834,7 +1020,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
|
||||
@@ -621,7 +621,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Create = (id, context, arch, groupArch) => new MultiBlend1D(id, context, arch, groupArch),
|
||||
Title = "Multi Blend 1D",
|
||||
Description = "Animation blending in 1D",
|
||||
Flags = NodeFlags.AnimGraph,
|
||||
Flags = NodeFlags.AnimGraph | NodeFlags.VariableValuesSize,
|
||||
Size = new Float2(420, 300),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
@@ -633,19 +633,6 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
// 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,
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
@@ -658,10 +645,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 3, 3),
|
||||
|
||||
// Axis X
|
||||
NodeElementArchetype.Factory.Input(4, "X", true, typeof(float), 4),
|
||||
NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY, "(min: max: )"),
|
||||
NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY, 0, 0),
|
||||
NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY, 0, 1),
|
||||
NodeElementArchetype.Factory.Input(3, "X", true, typeof(float), 4),
|
||||
NodeElementArchetype.Factory.Text(30, 3 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"),
|
||||
NodeElementArchetype.Factory.Float(60, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 0),
|
||||
NodeElementArchetype.Factory.Float(145, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 1),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
@@ -670,8 +657,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Create = (id, context, arch, groupArch) => new MultiBlend2D(id, context, arch, groupArch),
|
||||
Title = "Multi Blend 2D",
|
||||
Description = "Animation blending in 2D",
|
||||
Flags = NodeFlags.AnimGraph,
|
||||
Size = new Float2(420, 320),
|
||||
Flags = NodeFlags.AnimGraph | NodeFlags.VariableValuesSize,
|
||||
Size = new Float2(420, 620),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
// Node data
|
||||
@@ -682,19 +669,6 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
// 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,
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
@@ -707,16 +681,16 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 3, 3),
|
||||
|
||||
// Axis X
|
||||
NodeElementArchetype.Factory.Input(4, "X", true, typeof(float), 4),
|
||||
NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY, "(min: max: )"),
|
||||
NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY, 0, 0),
|
||||
NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY, 0, 1),
|
||||
NodeElementArchetype.Factory.Input(3, "X", true, typeof(float), 4),
|
||||
NodeElementArchetype.Factory.Text(30, 3 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"),
|
||||
NodeElementArchetype.Factory.Float(60, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 0),
|
||||
NodeElementArchetype.Factory.Float(145, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 1),
|
||||
|
||||
// Axis Y
|
||||
NodeElementArchetype.Factory.Input(5, "Y", true, typeof(float), 5),
|
||||
NodeElementArchetype.Factory.Text(30, 5 * Surface.Constants.LayoutOffsetY, "(min: max: )"),
|
||||
NodeElementArchetype.Factory.Float(60, 5 * Surface.Constants.LayoutOffsetY, 0, 2),
|
||||
NodeElementArchetype.Factory.Float(145, 5 * Surface.Constants.LayoutOffsetY, 0, 3),
|
||||
NodeElementArchetype.Factory.Input(4, "Y", true, typeof(float), 5),
|
||||
NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"),
|
||||
NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY + 2, 0, 2),
|
||||
NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY + 2, 0, 3),
|
||||
}
|
||||
},
|
||||
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)
|
||||
{
|
||||
base.OnLoaded(action);
|
||||
@@ -113,6 +123,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_type = TypeUtils.GetType(typeName);
|
||||
if (_type != null)
|
||||
{
|
||||
_type.TrackLifetime(OnTypeDisposing);
|
||||
TooltipText = Editor.Instance.CodeDocs.GetTooltip(_type);
|
||||
try
|
||||
{
|
||||
|
||||
@@ -73,6 +73,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
BehaviorTreeGraph = 1024,
|
||||
|
||||
/// <summary>
|
||||
/// Node can have different amount of items in values array.
|
||||
/// </summary>
|
||||
VariableValuesSize = 2048,
|
||||
|
||||
/// <summary>
|
||||
/// Node can be used in the all visual graphs.
|
||||
/// </summary>
|
||||
|
||||
@@ -951,15 +951,20 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
if (_isDuringValuesEditing || !Surface.CanEdit)
|
||||
return;
|
||||
|
||||
if (values == null || Values == null || values.Length != Values.Length)
|
||||
if (values == null || Values == null)
|
||||
throw new ArgumentException();
|
||||
bool resize = values.Length != Values.Length;
|
||||
if (resize && (Archetype.Flags & NodeFlags.VariableValuesSize) == 0)
|
||||
throw new ArgumentException();
|
||||
|
||||
_isDuringValuesEditing = true;
|
||||
|
||||
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();
|
||||
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 />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
@@ -1064,13 +1083,10 @@ namespace FlaxEditor.Surface
|
||||
return true;
|
||||
|
||||
// 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
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace FlaxEditor.Surface.Undo
|
||||
private ContextHandle _context;
|
||||
private readonly uint _nodeId;
|
||||
private readonly bool _graphEdited;
|
||||
private readonly bool _resize;
|
||||
private object[] _before;
|
||||
private object[] _after;
|
||||
|
||||
@@ -23,7 +24,8 @@ namespace FlaxEditor.Surface.Undo
|
||||
throw new ArgumentNullException(nameof(before));
|
||||
if (node?.Values == null)
|
||||
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));
|
||||
|
||||
_surface = node.Surface;
|
||||
@@ -48,7 +50,10 @@ namespace FlaxEditor.Surface.Undo
|
||||
throw new Exception("Missing node.");
|
||||
|
||||
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();
|
||||
context.MarkAsModified(_graphEdited);
|
||||
node.SetIsDuringValuesEditing(false);
|
||||
@@ -65,7 +70,10 @@ namespace FlaxEditor.Surface.Undo
|
||||
throw new Exception("Missing node.");
|
||||
|
||||
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();
|
||||
context.MarkAsModified(_graphEdited);
|
||||
node.SetIsDuringValuesEditing(false);
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public readonly InputActionsContainer InputActions;
|
||||
|
||||
/// <summary>
|
||||
/// Optional feature.
|
||||
/// </summary>
|
||||
public bool PanWithMiddleMouse = false;
|
||||
|
||||
private string _currentInputText = string.Empty;
|
||||
private Float2 _movingNodesDelta;
|
||||
private HashSet<SurfaceNode> _movingNodes;
|
||||
@@ -223,15 +228,18 @@ namespace FlaxEditor.Surface
|
||||
|
||||
if (_middleMouseDown)
|
||||
{
|
||||
// Calculate delta
|
||||
var delta = location - _middleMouseDownPos;
|
||||
if (delta.LengthSquared > 0.01f)
|
||||
if (PanWithMiddleMouse)
|
||||
{
|
||||
// Move view
|
||||
_mouseMoveAmount += delta.Length;
|
||||
_rootControl.Location += delta;
|
||||
_middleMouseDownPos = location;
|
||||
Cursor = CursorType.SizeAll;
|
||||
// Calculate delta
|
||||
var delta = location - _middleMouseDownPos;
|
||||
if (delta.LengthSquared > 0.01f)
|
||||
{
|
||||
// Move view
|
||||
_mouseMoveAmount += delta.Length;
|
||||
_rootControl.Location += delta;
|
||||
_middleMouseDownPos = location;
|
||||
Cursor = CursorType.SizeAll;
|
||||
}
|
||||
}
|
||||
|
||||
// Handled
|
||||
@@ -300,7 +308,8 @@ namespace FlaxEditor.Surface
|
||||
if (_middleMouseDown)
|
||||
{
|
||||
_middleMouseDown = false;
|
||||
Cursor = CursorType.Default;
|
||||
if (PanWithMiddleMouse)
|
||||
Cursor = CursorType.Default;
|
||||
}
|
||||
_isMovingSelection = false;
|
||||
ConnectingEnd(null);
|
||||
@@ -483,7 +492,7 @@ namespace FlaxEditor.Surface
|
||||
Focus();
|
||||
return true;
|
||||
}
|
||||
if (_rightMouseDown || _middleMouseDown)
|
||||
if (_rightMouseDown || (_middleMouseDown && _middleMouseDown))
|
||||
{
|
||||
// Start navigating
|
||||
StartMouseCapture();
|
||||
@@ -555,9 +564,12 @@ namespace FlaxEditor.Surface
|
||||
if (_middleMouseDown && button == MouseButton.Middle)
|
||||
{
|
||||
_middleMouseDown = false;
|
||||
EndMouseCapture();
|
||||
Cursor = CursorType.Default;
|
||||
if (_mouseMoveAmount > 0)
|
||||
if (_middleMouseDown)
|
||||
{
|
||||
EndMouseCapture();
|
||||
Cursor = CursorType.Default;
|
||||
}
|
||||
if (_mouseMoveAmount > 0 && _middleMouseDown)
|
||||
_mouseMoveAmount = 0;
|
||||
else if (CanEdit)
|
||||
{
|
||||
|
||||
@@ -630,6 +630,15 @@ namespace FlaxEditor.Surface
|
||||
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
|
||||
{
|
||||
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++)
|
||||
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
|
||||
{
|
||||
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
|
||||
|
||||
@@ -66,8 +66,8 @@ namespace FlaxEditor.Tools.Terrain
|
||||
[EditorOrder(410), EditorDisplay("Transform", "Rotation"), DefaultValue(typeof(Quaternion), "0,0,0,1"), Tooltip("Orientation of the terrain")]
|
||||
public Quaternion Orientation = Quaternion.Identity;
|
||||
|
||||
[EditorOrder(420), EditorDisplay("Transform", "Scale"), DefaultValue(typeof(Float3), "1,1,1"), Limit(float.MinValue, float.MaxValue, 0.01f), Tooltip("Scale of the terrain")]
|
||||
public Float3 Scale = Float3.One;
|
||||
[EditorOrder(420), EditorDisplay("Transform", "Scale"), DefaultValue(1.0f), Limit(0.0001f, float.MaxValue, 0.01f), Tooltip("Scale of the terrain")]
|
||||
public float Scale = 1.0f;
|
||||
}
|
||||
|
||||
private readonly Options _options = new Options();
|
||||
@@ -147,7 +147,7 @@ namespace FlaxEditor.Tools.Terrain
|
||||
// Create terrain object and setup some options
|
||||
var terrain = new FlaxEngine.Terrain();
|
||||
terrain.Setup(_options.LODCount, (int)_options.ChunkSize);
|
||||
terrain.Transform = new Transform(_options.Position, _options.Orientation, _options.Scale);
|
||||
terrain.Transform = new Transform(_options.Position, _options.Orientation, new Float3(_options.Scale));
|
||||
terrain.Material = _options.Material;
|
||||
terrain.CollisionLOD = _options.CollisionLOD;
|
||||
if (_options.Heightmap)
|
||||
@@ -238,24 +238,5 @@ namespace FlaxEditor.Tools.Terrain
|
||||
|
||||
return base.CanCloseWindow(reason);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (_isWorking)
|
||||
return true;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.Escape:
|
||||
OnCancel();
|
||||
return true;
|
||||
case KeyboardKeys.Return:
|
||||
OnSubmit();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ struct TextureDataResult
|
||||
}
|
||||
};
|
||||
|
||||
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data)
|
||||
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false)
|
||||
{
|
||||
// Lock asset chunks (if not virtual)
|
||||
data.Lock = texture->LockData();
|
||||
@@ -103,7 +103,7 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data)
|
||||
|
||||
// Decompress or convert data if need to
|
||||
data.Mip0DataPtr = &data.Mip0Data;
|
||||
if (PixelFormatExtensions::IsCompressed(data.Format))
|
||||
if (PixelFormatExtensions::IsCompressed(data.Format) || TextureTool::GetSampler(data.Format) == nullptr)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Decompress");
|
||||
|
||||
@@ -122,7 +122,7 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data)
|
||||
srcMip.Lines = src.Height;
|
||||
|
||||
// Decompress texture
|
||||
if (TextureTool::Convert(data.Tmp, src, PixelFormat::R8G8B8A8_UNorm))
|
||||
if (TextureTool::Convert(data.Tmp, src, hdr ? PixelFormat::R16G16B16A16_Float : PixelFormat::R8G8B8A8_UNorm))
|
||||
{
|
||||
LOG(Warning, "Failed to decompress data.");
|
||||
return true;
|
||||
@@ -134,7 +134,6 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data)
|
||||
data.SlicePitch = data.Tmp.Items[0].Mips[0].DepthPitch;
|
||||
data.Mip0DataPtr = &data.Tmp.Items[0].Mips[0].Data;
|
||||
}
|
||||
// TODO: convert to RGBA from other formats that cannot be sampled?
|
||||
|
||||
// Check if can even sample the given format
|
||||
const auto sampler = TextureTool::GetSampler(data.Format);
|
||||
@@ -155,7 +154,6 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
|
||||
LOG(Warning, "Cannot setup terain with no patches.");
|
||||
return false;
|
||||
}
|
||||
|
||||
PROFILE_CPU_NAMED("Terrain.GenerateTerrain");
|
||||
|
||||
// Wait for assets to be loaded
|
||||
@@ -188,7 +186,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
|
||||
{
|
||||
// Get data
|
||||
TextureDataResult dataHeightmap;
|
||||
if (GetTextureDataForSampling(heightmap, dataHeightmap))
|
||||
if (GetTextureDataForSampling(heightmap, dataHeightmap, true))
|
||||
return true;
|
||||
const auto sampler = TextureTool::GetSampler(dataHeightmap.Format);
|
||||
|
||||
@@ -198,7 +196,6 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
|
||||
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
|
||||
{
|
||||
auto patch = terrain->GetPatch(patchIndex);
|
||||
|
||||
const Vector2 uvStart = Vector2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
|
||||
|
||||
// Sample heightmap pixels with interpolation to get actual heightmap vertices locations
|
||||
|
||||
@@ -120,9 +120,9 @@ namespace FlaxEditor.Utilities
|
||||
/// <summary>
|
||||
/// Deletes the creates scenes for the simulation.
|
||||
/// </summary>
|
||||
public void DeletedScenes()
|
||||
public void UnloadScenes()
|
||||
{
|
||||
Profiler.BeginEvent("DuplicateScenes.DeletedScenes");
|
||||
Profiler.BeginEvent("DuplicateScenes.UnloadScenes");
|
||||
Editor.Log("Restoring scene data");
|
||||
|
||||
// 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.");
|
||||
}
|
||||
FlaxEngine.Scripting.FlushRemovedObjects();
|
||||
Editor.WipeOutLeftoverSceneObjects();
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
|
||||
@@ -210,12 +210,17 @@ namespace FlaxEditor.Utilities
|
||||
/// <returns>The duplicated value.</returns>
|
||||
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
|
||||
if (value != null && (!value.GetType().IsValueType || !value.GetType().IsClass))
|
||||
{
|
||||
var json = JsonSerializer.Serialize(value);
|
||||
value = JsonSerializer.Deserialize(json, value.GetType());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace FlaxEditor.Viewport
|
||||
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Float2 MouseDelta => _mouseDelta * 1000;
|
||||
public Float2 MouseDelta => _mouseDelta;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
|
||||
|
||||
@@ -65,6 +65,11 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
public bool IsAltDown;
|
||||
|
||||
/// <summary>
|
||||
/// The is alt down flag cached from the previous input. Used to make <see cref="IsControllingMouse"/> consistent when user releases Alt while orbiting with Alt+LMB.
|
||||
/// </summary>
|
||||
public bool WasAltDownBefore;
|
||||
|
||||
/// <summary>
|
||||
/// The is mouse right down flag.
|
||||
/// </summary>
|
||||
@@ -88,18 +93,20 @@ namespace FlaxEditor.Viewport
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether use is controlling mouse.
|
||||
/// </summary>
|
||||
public bool IsControllingMouse => IsMouseMiddleDown || IsMouseRightDown || (IsAltDown && IsMouseLeftDown) || Mathf.Abs(MouseWheelDelta) > 0.1f;
|
||||
public bool IsControllingMouse => IsMouseMiddleDown || IsMouseRightDown || ((IsAltDown || WasAltDownBefore) && IsMouseLeftDown) || Mathf.Abs(MouseWheelDelta) > 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gathers input from the specified window.
|
||||
/// </summary>
|
||||
/// <param name="window">The window.</param>
|
||||
/// <param name="useMouse">True if use mouse input, otherwise will skip mouse.</param>
|
||||
public void Gather(Window window, bool useMouse)
|
||||
/// <param name="prevInput">Previous input state.</param>
|
||||
public void Gather(Window window, bool useMouse, ref Input prevInput)
|
||||
{
|
||||
IsControlDown = window.GetKey(KeyboardKeys.Control);
|
||||
IsShiftDown = window.GetKey(KeyboardKeys.Shift);
|
||||
IsAltDown = window.GetKey(KeyboardKeys.Alt);
|
||||
WasAltDownBefore = prevInput.WasAltDownBefore || prevInput.IsAltDown;
|
||||
|
||||
IsMouseRightDown = useMouse && window.GetMouseButton(MouseButton.Right);
|
||||
IsMouseMiddleDown = useMouse && window.GetMouseButton(MouseButton.Middle);
|
||||
@@ -114,6 +121,7 @@ namespace FlaxEditor.Viewport
|
||||
IsControlDown = false;
|
||||
IsShiftDown = false;
|
||||
IsAltDown = false;
|
||||
WasAltDownBefore = false;
|
||||
|
||||
IsMouseRightDown = false;
|
||||
IsMouseMiddleDown = false;
|
||||
@@ -998,6 +1006,7 @@ namespace FlaxEditor.Viewport
|
||||
InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown);
|
||||
InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1));
|
||||
InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1));
|
||||
InputActions.Add(options => options.ToggleOrthographic, () => OnOrthographicModeToggled(null));
|
||||
|
||||
// Link for task event
|
||||
task.Begin += OnRenderBegin;
|
||||
@@ -1539,7 +1548,7 @@ namespace FlaxEditor.Viewport
|
||||
_prevInput = _input;
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
if (canUseInput && ContainsFocus && hit == null)
|
||||
_input.Gather(win.Window, useMouse);
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
_input.Clear();
|
||||
|
||||
@@ -1662,8 +1671,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
||||
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
||||
_mouseDelta = offset / size;
|
||||
_mouseDelta.Y *= size.Y / size.X;
|
||||
_mouseDelta = offset;
|
||||
|
||||
// Update delta filtering buffer
|
||||
_deltaFilteringBuffer[_deltaFilteringStep] = _mouseDelta;
|
||||
@@ -1681,8 +1689,7 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
_mouseDelta = offset / size;
|
||||
_mouseDelta.Y *= size.Y / size.X;
|
||||
_mouseDelta = offset;
|
||||
mouseDelta = _mouseDelta;
|
||||
}
|
||||
|
||||
@@ -1696,7 +1703,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Update
|
||||
moveDelta *= dt * (60.0f * 4.0f);
|
||||
mouseDelta *= 200.0f * MouseSpeed * _mouseSensitivity;
|
||||
mouseDelta *= 0.1833f * MouseSpeed * _mouseSensitivity;
|
||||
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
|
||||
|
||||
// Move mouse back to the root position
|
||||
@@ -1722,7 +1729,7 @@ namespace FlaxEditor.Viewport
|
||||
var offset = _viewMousePos - _startPos;
|
||||
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
|
||||
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
|
||||
_mouseDelta = offset / size;
|
||||
_mouseDelta = offset;
|
||||
_startPos = _viewMousePos;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -291,7 +291,7 @@ namespace FlaxEditor.Viewport
|
||||
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Float2 MouseDelta => _mouseDelta * 1000;
|
||||
public Float2 MouseDelta => _mouseDelta;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root.GetKey(KeyboardKeys.Control);
|
||||
|
||||
@@ -336,7 +336,8 @@ namespace FlaxEditor.Viewport.Previews
|
||||
if (_showNodes)
|
||||
{
|
||||
// Draw bounding box at the node locations
|
||||
var localBox = new OrientedBoundingBox(new Vector3(-1.0f), new Vector3(1.0f));
|
||||
var boxSize = Mathf.Min(1.0f, _previewModel.Sphere.Radius / 100.0f);
|
||||
var localBox = new OrientedBoundingBox(new Vector3(-boxSize), new Vector3(boxSize));
|
||||
for (int nodeIndex = 0; nodeIndex < pose.Length; nodeIndex++)
|
||||
{
|
||||
if (nodesMask != null && !nodesMask[nodeIndex])
|
||||
|
||||
@@ -115,7 +115,12 @@ namespace FlaxEditor.Viewport.Previews
|
||||
_hasUILinked = true;
|
||||
}
|
||||
else if (_uiControlLinked != null)
|
||||
{
|
||||
if (_uiControlLinked.Control != null &&
|
||||
_uiControlLinked.Control.Parent == null)
|
||||
_uiControlLinked.Control.Parent = _uiParentLink;
|
||||
_hasUILinked = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void LinkCanvas(Actor actor)
|
||||
|
||||
@@ -494,6 +494,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
_undo.Enabled = false;
|
||||
_undo.Clear();
|
||||
_propertiesEditor.BuildLayoutOnUpdate();
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -504,6 +505,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
_undo.Enabled = true;
|
||||
_undo.Clear();
|
||||
_propertiesEditor.BuildLayoutOnUpdate();
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -54,6 +54,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <param name="before">The selection before the change.</param>
|
||||
public void OnSelectionChanged(SceneGraphNode[] before)
|
||||
{
|
||||
if (LockSelectedObjects)
|
||||
return;
|
||||
|
||||
Undo.AddAction(new SelectionChangeAction(before, Selection.ToArray(), OnSelectionUndo));
|
||||
|
||||
OnSelectionChanges();
|
||||
|
||||
@@ -68,6 +68,11 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// </summary>
|
||||
public readonly LocalSceneGraph Graph;
|
||||
|
||||
/// <summary>
|
||||
/// Indication of if the prefab window selection is locked on specific objects.
|
||||
/// </summary>
|
||||
public bool LockSelectedObjects = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use live reloading for the prefab changes (applies prefab changes on modification by auto).
|
||||
/// </summary>
|
||||
|
||||
@@ -665,7 +665,16 @@ namespace FlaxEditor.Windows
|
||||
|
||||
// Scroll to the new entry (if any added to view)
|
||||
if (scrollView && anyVisible)
|
||||
{
|
||||
panelScroll.ScrollViewTo(newEntry);
|
||||
|
||||
bool scrollViewNew = (panelScroll.VScrollBar.Maximum - panelScroll.VScrollBar.TargetValue) < LogEntry.DefaultHeight * 1.5f;
|
||||
if (scrollViewNew != scrollView)
|
||||
{
|
||||
// Make sure scrolling doesn't stop in case too many entries were added at once
|
||||
panelScroll.ScrollViewTo(new Float2(float.MaxValue, float.MaxValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,11 @@ namespace FlaxEditor.Windows
|
||||
/// </summary>
|
||||
public bool UIPivotRelative = true;
|
||||
|
||||
/// <summary>
|
||||
/// Indication of if the properties window is locked on specific objects.
|
||||
/// </summary>
|
||||
public bool LockObjects = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PropertiesWindow"/> class.
|
||||
/// </summary>
|
||||
@@ -62,6 +67,9 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
if (LockObjects)
|
||||
return;
|
||||
|
||||
// Update selected objects
|
||||
// TODO: use cached collection for less memory allocations
|
||||
undoRecordObjects = Editor.SceneEditing.Selection.ConvertAll(x => x.UndoRecordObject).Distinct();
|
||||
|
||||
@@ -196,6 +196,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (actorType.IsAbstract)
|
||||
continue;
|
||||
_groupSearch.AddChild(CreateActorItem(Utilities.Utils.GetPropertyNameUI(actorType.Name), actorType));
|
||||
ActorToolboxAttribute attribute = null;
|
||||
foreach (var e in actorType.GetAttributes(false))
|
||||
{
|
||||
@@ -235,6 +236,7 @@ namespace FlaxEditor.Windows
|
||||
group.AddChild(string.IsNullOrEmpty(attribute.Name) ? CreateActorItem(Utilities.Utils.GetPropertyNameUI(actorType.Name), actorType) : CreateActorItem(attribute.Name, actorType));
|
||||
group.SortChildren();
|
||||
}
|
||||
_groupSearch.SortChildren();
|
||||
}
|
||||
|
||||
private void OnSearchBoxTextChanged()
|
||||
@@ -260,6 +262,10 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
|
||||
var text = (attribute == null) ? actorType.Name : string.IsNullOrEmpty(attribute.Name) ? actorType.Name : attribute.Name;
|
||||
|
||||
// Display all actors on no search
|
||||
if (string.IsNullOrEmpty(filterText))
|
||||
_groupSearch.AddChild(CreateActorItem(Utilities.Utils.GetPropertyNameUI(text), actorType));
|
||||
|
||||
if (!QueryFilterHelper.Match(filterText, text, out QueryFilterHelper.Range[] ranges))
|
||||
continue;
|
||||
@@ -278,6 +284,9 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
item.SetHighlights(highlights);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(filterText))
|
||||
_groupSearch.SortChildren();
|
||||
|
||||
_groupSearch.UnlockChildrenRecursive();
|
||||
PerformLayout();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/Pair.h"
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
#include "Engine/Animations/Curve.h"
|
||||
|
||||
@@ -68,6 +69,16 @@ public:
|
||||
uint64 GetMemoryUsage() const;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Single track with events.
|
||||
/// </summary>
|
||||
struct EventAnimationData
|
||||
{
|
||||
float Duration = 0.0f;
|
||||
StringAnsi TypeName;
|
||||
StringAnsi JsonData;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Root Motion modes that can be applied by the animation. Used as flags for selective behavior.
|
||||
/// </summary>
|
||||
@@ -120,10 +131,15 @@ struct AnimationData
|
||||
String RootNodeName;
|
||||
|
||||
/// <summary>
|
||||
/// The per skeleton node animation channels.
|
||||
/// The per-skeleton node animation channels.
|
||||
/// </summary>
|
||||
Array<NodeAnimationData> Channels;
|
||||
|
||||
/// <summary>
|
||||
/// The animation event tracks.
|
||||
/// </summary>
|
||||
Array<Pair<String, StepCurve<EventAnimationData>>> Events;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the length of the animation (in seconds).
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
|
||||
namespace
|
||||
{
|
||||
FORCE_INLINE bool CanUpdateModel(AnimatedModel* animatedModel)
|
||||
FORCE_INLINE bool CanUpdateModel(const AnimatedModel* animatedModel)
|
||||
{
|
||||
auto skinnedModel = animatedModel->SkinnedModel.Get();
|
||||
auto animGraph = animatedModel->AnimationGraph.Get();
|
||||
|
||||
@@ -117,11 +117,11 @@ void InstanceDataBucketInit(AnimGraphInstanceData::Bucket& bucket)
|
||||
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
|
||||
const auto aX = a == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? 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 aX = a == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + a * 2].AsFloat4().X;
|
||||
const auto bX = b == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + b * 2].AsFloat4().X;
|
||||
return aX < bX;
|
||||
}
|
||||
|
||||
@@ -133,8 +133,6 @@ bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
|
||||
bool AnimGraphBase::onNodeLoaded(Node* n)
|
||||
{
|
||||
((AnimGraphNode*)n)->Graph = _graph;
|
||||
|
||||
// Check if this node needs a state container
|
||||
switch (n->GroupID)
|
||||
{
|
||||
// Tools
|
||||
@@ -163,53 +161,51 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
|
||||
// Animation
|
||||
case 2:
|
||||
ADD_BUCKET(AnimationBucketInit);
|
||||
n->Assets.Resize(1);
|
||||
n->Assets[0] = (Asset*)Content::LoadAsync<Animation>((Guid)n->Values[0]);
|
||||
break;
|
||||
// Blend with Mask
|
||||
case 11:
|
||||
n->Assets.Resize(1);
|
||||
n->Assets[0] = (Asset*)Content::LoadAsync<SkeletonMask>((Guid)n->Values[1]);
|
||||
break;
|
||||
// Multi Blend 1D
|
||||
case 12:
|
||||
{
|
||||
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;
|
||||
const Float4 range = n->Values[0].AsFloat4();
|
||||
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
|
||||
n->Data.MultiBlend1D.IndicesSorted = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX) * n->Data.MultiBlend1D.Count);
|
||||
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->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;
|
||||
}
|
||||
// Multi Blend 2D
|
||||
case 13:
|
||||
{
|
||||
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;
|
||||
|
||||
// Get blend points locations
|
||||
Array<Float2, FixedAllocation<ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS + 3>> vertices;
|
||||
byte vertexIndexToAnimIndex[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS];
|
||||
const Float4 range = n->Values[0].AsFloat4();
|
||||
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
|
||||
Array<Float2> vertices;
|
||||
Array<ANIM_GRAPH_MULTI_BLEND_INDEX> vertexToAnim;
|
||||
n->Assets.Resize(n->Data.MultiBlend1D.Count);
|
||||
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])
|
||||
{
|
||||
const int32 vertexIndex = vertices.Count();
|
||||
vertexIndexToAnimIndex[vertexIndex] = i;
|
||||
vertices.Add(Float2(n->Values[i * 2 + 4].AsFloat4()));
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexIndexToAnimIndex[i] = -1;
|
||||
vertexToAnim.Add((ANIM_GRAPH_MULTI_BLEND_INDEX)i);
|
||||
}
|
||||
}
|
||||
|
||||
// Triangulate
|
||||
Array<Delaunay2D::Triangle, FixedAllocation<ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS>> triangles;
|
||||
Array<Delaunay2D::Triangle> triangles;
|
||||
Delaunay2D::Triangulate(vertices, triangles);
|
||||
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)
|
||||
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.TrianglesP1[i] = vertexIndexToAnimIndex[triangles[i].Indices[1]];
|
||||
n->Data.MultiBlend2D.TrianglesP2[i] = vertexIndexToAnimIndex[triangles[i].Indices[2]];
|
||||
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[0]];
|
||||
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[1]];
|
||||
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;
|
||||
}
|
||||
// Blend Pose
|
||||
@@ -307,6 +302,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
|
||||
data.Graph = nullptr;
|
||||
break;
|
||||
}
|
||||
n->Assets.Resize(1);
|
||||
n->Assets[0] = function;
|
||||
|
||||
// Load the graph
|
||||
@@ -384,6 +380,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
|
||||
void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Value& transitionsData)
|
||||
{
|
||||
int32 validTransitions = 0;
|
||||
data.Transitions = nullptr;
|
||||
if (transitionsData.Type == VariantType::Blob && 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;
|
||||
stream.ReadInt32(&transitionsCount);
|
||||
if (transitionsCount == 0)
|
||||
return;
|
||||
|
||||
StateTransitions.EnsureCapacity(StateTransitions.Count() + transitionsCount);
|
||||
data.Transitions = (uint16*)Allocator::Allocate(transitionsCount * sizeof(uint16));
|
||||
|
||||
AnimGraphStateTransition transition;
|
||||
for (int32 i = 0; i < transitionsCount; i++)
|
||||
@@ -434,7 +434,6 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val
|
||||
// Skip disabled transitions
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ruleSize != 0)
|
||||
{
|
||||
transition.RuleGraph = LoadSubGraph(ruleBytes, ruleSize, TEXT("Rule"));
|
||||
@@ -444,25 +443,19 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (transition.Destination == nullptr)
|
||||
{
|
||||
LOG(Warning, "Missing target node for the state machine transition.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (validTransitions == ANIM_GRAPH_MAX_STATE_TRANSITIONS)
|
||||
{
|
||||
LOG(Warning, "State uses too many transitions.");
|
||||
continue;
|
||||
}
|
||||
|
||||
data.Transitions[validTransitions++] = (uint16)StateTransitions.Count();
|
||||
StateTransitions.Add(transition);
|
||||
}
|
||||
}
|
||||
if (validTransitions != ANIM_GRAPH_MAX_STATE_TRANSITIONS)
|
||||
|
||||
// Last entry is invalid to indicate end
|
||||
data.Transitions[validTransitions] = AnimGraphNode::StateData::InvalidTransitionIndex;
|
||||
}
|
||||
|
||||
// Release data to don't use that memory
|
||||
transitionsData = AnimGraphExecutor::Value::Null;
|
||||
|
||||
@@ -102,6 +102,34 @@ AnimGraphInstanceData::OutgoingEvent AnimGraphInstanceData::ActiveEvent::End(Ani
|
||||
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)
|
||||
{
|
||||
auto& context = *AnimGraphExecutor::Context.Get();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Visject/VisjectGraph.h"
|
||||
#include "Engine/Content/Assets/Animation.h"
|
||||
#include "Engine/Core/Collections/ChunkedArray.h"
|
||||
#include "Engine/Core/Collections/BitArray.h"
|
||||
#include "Engine/Animations/AlphaBlend.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include "../Config.h"
|
||||
@@ -12,9 +13,8 @@
|
||||
#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_MULTI_BLEND_MAX_ANIMS 14
|
||||
#define ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS 32
|
||||
#define ANIM_GRAPH_MAX_STATE_TRANSITIONS 64
|
||||
#define ANIM_GRAPH_MULTI_BLEND_INDEX byte
|
||||
#define ANIM_GRAPH_MULTI_BLEND_INVALID 0xff
|
||||
#define ANIM_GRAPH_MAX_CALL_STACK 100
|
||||
#define ANIM_GRAPH_MAX_EVENTS 64
|
||||
|
||||
@@ -426,61 +426,31 @@ struct AnimGraphTransitionData
|
||||
float Length;
|
||||
};
|
||||
|
||||
class AnimGraphBox : public VisjectGraphBox
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
};
|
||||
typedef VisjectGraphBox AnimGraphBox;
|
||||
|
||||
class AnimGraphNode : public VisjectGraphNode<AnimGraphBox>
|
||||
{
|
||||
public:
|
||||
struct MultiBlend1DData
|
||||
{
|
||||
/// <summary>
|
||||
/// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
|
||||
/// </summary>
|
||||
// Amount of blend points.
|
||||
ANIM_GRAPH_MULTI_BLEND_INDEX Count;
|
||||
// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
|
||||
float Length;
|
||||
|
||||
/// <summary>
|
||||
/// 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];
|
||||
// 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.
|
||||
ANIM_GRAPH_MULTI_BLEND_INDEX* IndicesSorted;
|
||||
};
|
||||
|
||||
struct MultiBlend2DData
|
||||
{
|
||||
/// <summary>
|
||||
/// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
|
||||
/// </summary>
|
||||
// Amount of blend points.
|
||||
ANIM_GRAPH_MULTI_BLEND_INDEX Count;
|
||||
// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
|
||||
float Length;
|
||||
|
||||
/// <summary>
|
||||
/// Cached triangles vertices (vertex 0). Contains list of indices for triangles to use for blending.
|
||||
/// </summary>
|
||||
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];
|
||||
// Amount of triangles.
|
||||
int32 TrianglesCount;
|
||||
// Cached triangles vertices (3 bytes per triangle). Contains list of indices for triangles to use for blending.
|
||||
ANIM_GRAPH_MULTI_BLEND_INDEX* Triangles;
|
||||
};
|
||||
|
||||
struct StateMachineData
|
||||
@@ -493,15 +463,11 @@ public:
|
||||
|
||||
struct StateBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// The invalid transition valid used in Transitions to indicate invalid transition linkage.
|
||||
/// </summary>
|
||||
// The invalid transition valid used in Transitions to indicate invalid transition linkage.
|
||||
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.
|
||||
/// </summary>
|
||||
uint16 Transitions[ANIM_GRAPH_MAX_STATE_TRANSITIONS];
|
||||
// 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;
|
||||
};
|
||||
|
||||
struct StateData : StateBaseData
|
||||
@@ -597,12 +563,14 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~AnimGraphNode();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the per-node node transformations cache (cached).
|
||||
/// </summary>
|
||||
/// <param name="executor">The Graph execution context.</param>
|
||||
/// <returns>The modes data.</returns>
|
||||
/// <returns>Nodes data.</returns>
|
||||
AnimGraphImpulse* GetNodes(AnimGraphExecutor* executor);
|
||||
};
|
||||
|
||||
@@ -799,7 +767,7 @@ struct AnimGraphContext
|
||||
bool StackOverFlow;
|
||||
Array<VisjectExecutor::Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> CallStack;
|
||||
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;
|
||||
ChunkedArray<AnimGraphImpulse, 256> PoseCache;
|
||||
int32 PoseCacheSize;
|
||||
@@ -892,7 +860,7 @@ private:
|
||||
|
||||
int32 GetRootNodeIndex(Animation* anim);
|
||||
void ProcessAnimEvents(AnimGraphNode* node, bool loop, float length, float animPos, float animPrevPos, Animation* anim, float speed);
|
||||
void ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight = 1.0f, ProcessAnimationMode mode = ProcessAnimationMode::Override);
|
||||
void ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight = 1.0f, ProcessAnimationMode mode = ProcessAnimationMode::Override, BitArray<InlinedAllocation<8>>* usedNodes = nullptr);
|
||||
Variant SampleAnimation(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* anim, float speed);
|
||||
Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, float speedA, float speedB, float alpha);
|
||||
Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, Animation* animC, float speedA, float speedB, float speedC, float alphaA, float alphaB, float alphaC);
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace
|
||||
{
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
nodes->Nodes[i].Orientation.Normalize();
|
||||
nodes->Nodes.Get()[i].Orientation.Normalize();
|
||||
}
|
||||
if (rootMotionMode != RootMotionExtraction::NoExtraction)
|
||||
{
|
||||
@@ -222,7 +222,7 @@ FORCE_INLINE void GetAnimSamplePos(bool loop, float length, float startTimePos,
|
||||
prevPos = GetAnimPos(prevTimePos, startTimePos, loop, length);
|
||||
}
|
||||
|
||||
void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight, ProcessAnimationMode mode)
|
||||
void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight, ProcessAnimationMode mode, BitArray<InlinedAllocation<8>>* usedNodes)
|
||||
{
|
||||
PROFILE_CPU_ASSET(anim);
|
||||
|
||||
@@ -240,9 +240,17 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
}
|
||||
|
||||
// Evaluate nested animations
|
||||
bool hasNested = false;
|
||||
BitArray<InlinedAllocation<8>> usedNodesThis;
|
||||
if (anim->NestedAnims.Count() != 0)
|
||||
{
|
||||
if (usedNodes == nullptr)
|
||||
{
|
||||
// Per-channel bit to indicate which channels were used by nested
|
||||
usedNodesThis.Resize(nodes->Nodes.Count());
|
||||
usedNodesThis.SetAll(false);
|
||||
usedNodes = &usedNodesThis;
|
||||
}
|
||||
|
||||
for (auto& e : anim->NestedAnims)
|
||||
{
|
||||
const auto& nestedAnim = e.Second;
|
||||
@@ -262,8 +270,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
nestedAnimPrevPos = nestedAnimPrevPos * frameRateMatchScale;
|
||||
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
|
||||
|
||||
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode);
|
||||
hasNested = true;
|
||||
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode, usedNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,11 +287,11 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
SkinnedModel::SkeletonMapping sourceMapping;
|
||||
if (retarget)
|
||||
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];
|
||||
Transform& dstNode = nodes->Nodes[i];
|
||||
Transform srcNode = emptyNodes->Nodes[i];
|
||||
const int32 nodeToChannel = mapping.NodesMapping[nodeIndex];
|
||||
Transform& dstNode = nodes->Nodes[nodeIndex];
|
||||
Transform srcNode = emptyNodes->Nodes[nodeIndex];
|
||||
if (nodeToChannel != -1)
|
||||
{
|
||||
// Calculate the animated node transformation
|
||||
@@ -293,8 +300,17 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
// Optionally retarget animation into the skeleton used by the Anim Graph
|
||||
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
|
||||
if (usedNodes)
|
||||
usedNodes->Set(nodeIndex, true);
|
||||
}
|
||||
else if (usedNodes && (usedNodes != &usedNodesThis || usedNodes->Get(nodeIndex)))
|
||||
{
|
||||
// Skip for nested animations so other one or top-level anim will update remaining nodes
|
||||
continue;
|
||||
}
|
||||
|
||||
// Blend node
|
||||
@@ -316,7 +332,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
dstNode.Scale = srcNode.Scale * weight;
|
||||
dstNode.Orientation = srcNode.Orientation * weight;
|
||||
}
|
||||
else if (!hasNested)
|
||||
else
|
||||
{
|
||||
dstNode = srcNode;
|
||||
}
|
||||
@@ -570,7 +586,7 @@ AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphCon
|
||||
AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, const AnimGraphNode::StateBaseData& stateData, AnimGraphNode* state, AnimGraphNode* ignoreState)
|
||||
{
|
||||
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];
|
||||
ASSERT(idx < stateMachineData.Graph->StateTransitions.Count());
|
||||
@@ -658,19 +674,20 @@ void ComputeMultiBlendLength(float& length, AnimGraphNode* node)
|
||||
// TODO: lock graph or graph asset here? make it thread safe
|
||||
|
||||
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?
|
||||
if (node->Assets[i]->WaitForLoaded())
|
||||
if (asset->WaitForLoaded())
|
||||
{
|
||||
node->Assets[i] = nullptr;
|
||||
asset = nullptr;
|
||||
LOG(Warning, "Failed to load one of the animations.");
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto anim = node->Assets[i].As<Animation>();
|
||||
const auto anim = asset.As<Animation>();
|
||||
const auto aData = node->Values[4 + i * 2].AsFloat4();
|
||||
length = Math::Max(length, anim->GetLength() * Math::Abs(aData.W));
|
||||
}
|
||||
@@ -1177,14 +1194,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
const float alpha = Math::Saturate((float)tryGetValue(node->GetBox(3), node->Values[0]));
|
||||
auto mask = node->Assets[0].As<SkeletonMask>();
|
||||
auto maskAssetBox = node->GetBox(4); // 4 is the id of skeleton mask parameter node.
|
||||
|
||||
// Check if have some mask asset connected with the mask node
|
||||
if (maskAssetBox->HasConnection())
|
||||
// Use the mask connected with this node instead of default mask asset
|
||||
auto maskAssetBox = node->TryGetBox(4);
|
||||
if (maskAssetBox && maskAssetBox->HasConnection())
|
||||
{
|
||||
const Value assetBoxValue = tryGetValue(maskAssetBox, Value::Null);
|
||||
|
||||
// Use the mask connected with this node instead of default mask asset
|
||||
if (assetBoxValue != Value::Null)
|
||||
mask = (SkeletonMask*)assetBoxValue.AsAsset;
|
||||
}
|
||||
@@ -1255,13 +1270,20 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
auto& data = node->Data.MultiBlend1D;
|
||||
|
||||
// Check if not valid animation binded
|
||||
if (data.IndicesSorted[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
|
||||
if (data.Count == 0)
|
||||
break;
|
||||
|
||||
// Get axis X
|
||||
float x = (float)tryGetValue(node->GetBox(4), Value::Zero);
|
||||
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
|
||||
if (data.Length < 0)
|
||||
ComputeMultiBlendLength(data.Length, node);
|
||||
@@ -1279,7 +1301,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
ANIM_GRAPH_PROFILE_EVENT("Multi Blend 1D");
|
||||
|
||||
// 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 b = data.IndicesSorted[i + 1];
|
||||
@@ -1289,14 +1311,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
auto aData = node->Values[4 + a * 2].AsFloat4();
|
||||
|
||||
// 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);
|
||||
break;
|
||||
}
|
||||
|
||||
// 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>();
|
||||
auto bData = node->Values[4 + b * 2].AsFloat4();
|
||||
|
||||
@@ -1344,7 +1366,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
auto& data = node->Data.MultiBlend2D;
|
||||
|
||||
// Check if not valid animation binded
|
||||
if (data.TrianglesP0[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
|
||||
if (data.TrianglesCount == 0)
|
||||
break;
|
||||
|
||||
// Get axis X
|
||||
@@ -1355,6 +1377,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
float y = (float)tryGetValue(node->GetBox(5), Value::Zero);
|
||||
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
|
||||
if (data.Length < 0)
|
||||
ComputeMultiBlendLength(data.Length, node);
|
||||
@@ -1377,20 +1407,20 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
Float2 bestPoint;
|
||||
float bestWeight = 0.0f;
|
||||
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
|
||||
const auto a = data.TrianglesP0[i];
|
||||
const auto a = data.Triangles[t++];
|
||||
const auto aAnim = node->Assets[a].As<Animation>();
|
||||
const auto aData = node->Values[4 + a * 2].AsFloat4();
|
||||
|
||||
// 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 bData = node->Values[4 + b * 2].AsFloat4();
|
||||
|
||||
// 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 cData = node->Values[4 + c * 2].AsFloat4();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user