Compare commits
337 Commits
1.8.6510
...
1.8.6512.2
| Author | SHA1 | Date | |
|---|---|---|---|
| c486577b07 | |||
| a69c8ce6a2 | |||
| e527783e55 | |||
| 4d9c92dd49 | |||
| 9b01229e58 | |||
| e3a030fad8 | |||
| cb878294ea | |||
| 1bd1aca0f0 | |||
| b433312042 | |||
| 1041b1b86d | |||
| 1ace5fd10d | |||
| cfc9f73744 | |||
| 2418167182 | |||
| 52090d3a6b | |||
| 73f68c102d | |||
| 862dd1e5f1 | |||
| 58351d1989 | |||
| 6705205e2f | |||
| 2cdd0ff644 | |||
| 69ae841f64 | |||
| 7f8700288f | |||
| f87dec6ca6 | |||
| 65a6c0aed5 | |||
| f6dd0decfb | |||
| 816984542a | |||
| 3837e8b263 | |||
| 47b3141f18 | |||
| e10ee3e55a | |||
| 0a4e89e29b | |||
| 0765fa92b5 | |||
| 2529312152 | |||
| 3404643636 | |||
| 6b9f6ac82e | |||
| ab5bb79754 | |||
| 58f95d6ce3 | |||
| 1cd2f6a070 | |||
| 35ddfc2455 | |||
| 17d1d87268 | |||
| fd871ce830 | |||
| b4a4a8a591 | |||
| 24e4015425 | |||
| c670887b1a | |||
| d3cd6a461b | |||
| 2625a9d762 | |||
| 4fdd9a242b | |||
| a6e8e6f749 | |||
| 03eabbcf63 | |||
| 13f94dcf11 | |||
| 3b44062eb0 | |||
| 1457637707 | |||
| d28567111f | |||
| 1c7f06e570 | |||
| 63cc0fef2e | |||
| 57084b3d6c | |||
| fa23619f08 | |||
| 1f2456fc67 | |||
| 0b71e906a6 | |||
| 2e59c35a44 | |||
| 6f2bd0e932 | |||
| 6a883bc7c6 | |||
| 17de6388ca | |||
| e028d263f1 | |||
| 6962ed6730 | |||
| b66d50ae1b | |||
| 675ce71935 | |||
| 833f844d59 | |||
| af08dc1c69 | |||
| 07628d2ec7 | |||
| aac3dbfe09 | |||
| 185f24ce49 | |||
| 77e29109ee | |||
| 8d89b9efb0 | |||
| b2fee31a13 | |||
| fc7628e2ee | |||
| 2e3e4959d6 | |||
| f22105c2c3 | |||
| 533902d185 | |||
| 68653fa91f | |||
| dc0aa61a14 | |||
| ee790ff3a9 | |||
| a2a3926aee | |||
| 9a70344c1f | |||
| 44006dd533 | |||
| f6aabf2d14 | |||
| dc1f15f18d | |||
| 7d7808af8f | |||
| 5029584a9f | |||
| f353d3f114 | |||
| 667e8bc293 | |||
| 2edb9cc4d8 | |||
| 7018666a8c | |||
| f04926ad94 | |||
| 50f5f0acd9 | |||
| 3745979b81 | |||
| db15f6f08a | |||
| e1a2f51d5a | |||
| a8e1fd7a4a | |||
| d46ef6ac92 | |||
| 36d21b27c7 | |||
| b1636c27e7 | |||
| 5d32fc6c5e | |||
| 065dc474c0 | |||
| 1fb7b24aad | |||
| f0b72aa025 | |||
| 058077736b | |||
| b02f011627 | |||
| ea04c746fd | |||
| 97454fc82e | |||
| 4a6afdb108 | |||
| 65e852600a | |||
| fedd990c13 | |||
| c0329abe40 | |||
| d8850a56a8 | |||
| e171bb06ec | |||
| 3825e07adc | |||
| db8adf7d96 | |||
| e77ae12b9b | |||
| bf4e4aeaf6 | |||
| 2107b069db | |||
| ea2005dacb | |||
| d5cded8aaa | |||
| 430b22d5d7 | |||
| 9d830eb1e2 | |||
| 7e3f84f95e | |||
| cddee38d71 | |||
| e030d0461b | |||
| 4978c8e0d9 | |||
| dc7b7e6e10 | |||
| 1e3eb11b94 | |||
| b15b231b85 | |||
| 262992571a | |||
| 352bf3f9a7 | |||
| 9683868767 | |||
| 40284fbbf8 | |||
| 0c86a900da | |||
| c1e3eaeab1 | |||
| 3c487dff47 | |||
| 2260d79e26 | |||
| 3209320547 | |||
| d1db06a9bb | |||
| 1c1d2fd96f | |||
| 2e5ad8c48a | |||
| 9a6f866956 | |||
| c59bce3b58 | |||
| 1185a9c06c | |||
| 026b69c544 | |||
| df7ece7655 | |||
| 7e8f20bd9b | |||
| 20aef27439 | |||
| 12344cf725 | |||
| ebb2704726 | |||
| 5f9d0140c7 | |||
| 02a7f74993 | |||
| 452e12db45 | |||
| ac46c89904 | |||
| 88acc772b3 | |||
| fbec80c801 | |||
| 1ef487a5cc | |||
| 1e5861de25 | |||
| 07733a4efb | |||
| fee15846ba | |||
| 7040da9a44 | |||
| a8a621df3b | |||
| 18660140b0 | |||
| 58cc53b44d | |||
| d8bb831dd9 | |||
| 0d770e3909 | |||
| 3641886ebf | |||
| 611dd7bad1 | |||
| 7db20e0411 | |||
| e835b25637 | |||
| 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": 2,
|
||||
"Build": 6512
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -74,11 +74,15 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=UNION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=UNION_005FMEMBER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ARGB/@EntryIndexedValue">ARGB</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LO/@EntryIndexedValue">LO</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RPC/@EntryIndexedValue">RPC</s:String>
|
||||
<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 +224,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 +237,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");
|
||||
@@ -341,7 +365,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
|
||||
Platform::CreateProcess(procSettings);
|
||||
}
|
||||
#endif
|
||||
const bool distributionPackage = buildSettings->ForDistribution;
|
||||
const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release;
|
||||
{
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
|
||||
|
||||
@@ -536,7 +536,7 @@ namespace FlaxEditor.CustomEditors
|
||||
/// </summary>
|
||||
public void ClearReferenceValueAll()
|
||||
{
|
||||
Values.ClearReferenceValue();
|
||||
Values?.ClearReferenceValue();
|
||||
|
||||
for (int i = 0; i < ChildrenEditors.Count; i++)
|
||||
{
|
||||
|
||||
@@ -52,13 +52,18 @@ namespace FlaxEditor.CustomEditors
|
||||
// Check if use provided editor
|
||||
if (overrideEditor != null)
|
||||
return overrideEditor;
|
||||
ScriptType targetType = values.Type;
|
||||
|
||||
// Special case if property is a pure object type and all values are the same type
|
||||
if (values.Type.Type == typeof(object) && values.Count > 0 && values[0] != null && !values.HasDifferentTypes)
|
||||
if (targetType.Type == typeof(object) && values.Count > 0 && values[0] != null && !values.HasDifferentTypes)
|
||||
return CreateEditor(TypeUtils.GetObjectType(values[0]), canUseRefPicker);
|
||||
|
||||
// Special case if property is interface but the value is implemented as Scripting Object that should use reference picker
|
||||
if (targetType.IsInterface && canUseRefPicker && values.Count > 0 && values[0] is FlaxEngine.Object)
|
||||
return new DummyEditor();
|
||||
|
||||
// Use editor for the property type
|
||||
return CreateEditor(values.Type, canUseRefPicker);
|
||||
return CreateEditor(targetType, canUseRefPicker);
|
||||
}
|
||||
|
||||
internal static CustomEditor CreateEditor(ScriptType targetType, bool canUseRefPicker = true)
|
||||
|
||||
@@ -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;
|
||||
@@ -72,7 +75,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
// Selecting actor prefab asset
|
||||
var selectPrefab = panel.Button("Select Prefab");
|
||||
selectPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Select(prefab);
|
||||
selectPrefab.Button.Clicked += () =>
|
||||
{
|
||||
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
|
||||
Editor.Instance.Windows.ContentWin.Select(prefab);
|
||||
};
|
||||
|
||||
// Viewing changes applied to this actor
|
||||
var viewChanges = panel.Button("View Changes");
|
||||
@@ -111,6 +118,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 +203,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 +295,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];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -12,7 +13,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
[CustomEditor(typeof(LayersMask)), DefaultEditor]
|
||||
internal class LayersMaskEditor : CustomEditor
|
||||
{
|
||||
private CheckBox[] _checkBoxes;
|
||||
private List<CheckBox> _checkBoxes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -24,16 +25,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
return;
|
||||
}
|
||||
|
||||
_checkBoxes = new CheckBox[layers.Length];
|
||||
_checkBoxes = new List<CheckBox>();
|
||||
for (int i = 0; i < layers.Length; i++)
|
||||
{
|
||||
var layer = layers[i];
|
||||
var property = layout.AddPropertyItem(layer);
|
||||
if (string.IsNullOrEmpty(layer))
|
||||
continue;
|
||||
var property = layout.AddPropertyItem($"{i}: {layer}");
|
||||
var checkbox = property.Checkbox().CheckBox;
|
||||
UpdateCheckbox(checkbox, i);
|
||||
checkbox.Tag = i;
|
||||
checkbox.StateChanged += OnCheckboxStateChanged;
|
||||
_checkBoxes[i] = checkbox;
|
||||
_checkBoxes.Add(checkbox);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +53,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
if (_checkBoxes != null)
|
||||
{
|
||||
for (int i = 0; i < _checkBoxes.Length; i++)
|
||||
for (int i = 0; i < _checkBoxes.Count; i++)
|
||||
{
|
||||
UpdateCheckbox(_checkBoxes[i], i);
|
||||
UpdateCheckbox(_checkBoxes[i], (int)_checkBoxes[i].Tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,14 +456,57 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
for (int i = 0; i < layout.Children.Count; i++)
|
||||
{
|
||||
if (layout.Children[i] is GroupElement group && group.Panel.HeaderText == "Transform")
|
||||
if (layout.Children[i] is GroupElement group && group.Panel.HeaderText.Equals("Transform", StringComparison.Ordinal))
|
||||
{
|
||||
VerticalPanelElement mainHor = VerticalPanelWithoutMargin(group);
|
||||
CreateTransformElements(mainHor, ValuesTypes);
|
||||
group.ContainerControl.ChangeChildIndex(mainHor.Control, 0);
|
||||
layout.Children.Remove(group);
|
||||
layout.ContainerControl.Children.Remove(group.Panel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup transform
|
||||
if (Presenter is LayoutElementsContainer l)
|
||||
{
|
||||
for (int i = 0; i < l.Children.Count; i++)
|
||||
{
|
||||
if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("Transform", StringComparison.Ordinal))
|
||||
{
|
||||
l.Children.Remove(g);
|
||||
l.ContainerControl.Children.Remove(g.Panel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var transformGroup = l.Group("Transform");
|
||||
VerticalPanelElement mainHor = VerticalPanelWithoutMargin(transformGroup);
|
||||
CreateTransformElements(mainHor, ValuesTypes);
|
||||
|
||||
ScriptMemberInfo scaleInfo = ValuesTypes[0].GetProperty("Scale");
|
||||
ItemInfo scaleItem = new ItemInfo(scaleInfo);
|
||||
transformGroup.Property("Scale", scaleItem.GetValues(Values));
|
||||
|
||||
ScriptMemberInfo pivotInfo = ValuesTypes[0].GetProperty("Pivot");
|
||||
ItemInfo pivotItem = new ItemInfo(pivotInfo);
|
||||
transformGroup.Property("Pivot", pivotItem.GetValues(Values));
|
||||
|
||||
ScriptMemberInfo shearInfo = ValuesTypes[0].GetProperty("Shear");
|
||||
ItemInfo shearItem = new ItemInfo(shearInfo);
|
||||
transformGroup.Property("Shear", shearItem.GetValues(Values));
|
||||
|
||||
ScriptMemberInfo rotationInfo = ValuesTypes[0].GetProperty("Rotation");
|
||||
ItemInfo rotationItem = new ItemInfo(rotationInfo);
|
||||
transformGroup.Property("Rotation", rotationItem.GetValues(Values));
|
||||
|
||||
// Get position of general tab
|
||||
for (int i = 0; i < l.Children.Count; i++)
|
||||
{
|
||||
if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("General", StringComparison.Ordinal) && i + 1 <= l.Children.Count)
|
||||
{
|
||||
Presenter.ContainerControl.ChangeChildIndex(transformGroup.Control, i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTransformElements(LayoutElementsContainer main, ScriptType[] valueTypes)
|
||||
@@ -645,7 +688,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
var grid = UniformGridTwoByOne(el);
|
||||
grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1);
|
||||
var label = grid.Label(text);
|
||||
var label = grid.Label(text, TextAlignment.Far);
|
||||
var editor = grid.Object(values);
|
||||
if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement)
|
||||
{
|
||||
|
||||
@@ -57,17 +57,18 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
menu.ItemsContainer.RemoveChildren();
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste;
|
||||
var b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
menu.AddSeparator();
|
||||
var moveUpButton = menu.AddButton("Move up", OnMoveUpClicked);
|
||||
moveUpButton.Enabled = Index > 0;
|
||||
b = menu.AddButton("Move up", OnMoveUpClicked);
|
||||
b.Enabled = Index > 0 && !Editor._readOnly;
|
||||
|
||||
var moveDownButton = menu.AddButton("Move down", OnMoveDownClicked);
|
||||
moveDownButton.Enabled = Index + 1 < Editor.Count;
|
||||
|
||||
menu.AddButton("Remove", OnRemoveClicked);
|
||||
b = menu.AddButton("Move down", OnMoveDownClicked);
|
||||
b.Enabled = Index + 1 < Editor.Count && !Editor._readOnly;
|
||||
|
||||
b = menu.AddButton("Remove", OnRemoveClicked);
|
||||
b.Enabled = !Editor._readOnly;
|
||||
}
|
||||
|
||||
private void OnMoveUpClicked()
|
||||
@@ -177,6 +178,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
private IntValueBox _sizeBox;
|
||||
private Color _background;
|
||||
private int _elementsCount, _minCount, _maxCount;
|
||||
private bool _readOnly;
|
||||
private bool _canResize;
|
||||
private bool _canReorderItems;
|
||||
private CollectionAttribute.DisplayType _displayType;
|
||||
@@ -209,6 +211,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
return;
|
||||
|
||||
var size = Count;
|
||||
_readOnly = false;
|
||||
_canResize = true;
|
||||
_canReorderItems = true;
|
||||
_minCount = 0;
|
||||
@@ -224,7 +227,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute);
|
||||
if (collection != null)
|
||||
{
|
||||
_canResize = !collection.ReadOnly;
|
||||
_canResize = collection.CanResize;
|
||||
_readOnly = collection.ReadOnly;
|
||||
_minCount = collection.MinCount;
|
||||
_maxCount = collection.MaxCount;
|
||||
_canReorderItems = collection.CanReorderItems;
|
||||
@@ -235,6 +239,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
spacing = collection.Spacing;
|
||||
_displayType = collection.Display;
|
||||
}
|
||||
if (attributes != null && attributes.Any(x => x is ReadOnlyAttribute))
|
||||
{
|
||||
_readOnly = true;
|
||||
_canResize = false;
|
||||
_canReorderItems = false;
|
||||
}
|
||||
if (_maxCount == 0)
|
||||
_maxCount = ushort.MaxValue;
|
||||
_canResize &= _minCount < _maxCount;
|
||||
@@ -243,8 +253,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
dragArea.CustomControl.Editor = this;
|
||||
dragArea.CustomControl.ElementType = ElementType;
|
||||
|
||||
// Check for the AssetReferenceAttribute. In JSON assets, it can be used to filter
|
||||
// which scripts can be dragged over and dropped on this collection editor.
|
||||
// Check for the AssetReferenceAttribute. In JSON assets, it can be used to filter which scripts can be dragged over and dropped on this collection editor
|
||||
var assetReference = (AssetReferenceAttribute)attributes?.FirstOrDefault(x => x is AssetReferenceAttribute);
|
||||
if (assetReference != null)
|
||||
{
|
||||
@@ -333,6 +342,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var property = panel.AddPropertyItem(itemLabel);
|
||||
var itemLayout = (LayoutElementsContainer)property;
|
||||
itemLabel.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor);
|
||||
if (_readOnly && itemLayout.Children.Count > 0)
|
||||
GenericEditor.OnReadOnlyProperty(itemLayout);
|
||||
}
|
||||
else if (_displayType == CollectionAttribute.DisplayType.Header || (_displayType == CollectionAttribute.DisplayType.Default && !single))
|
||||
{
|
||||
@@ -340,13 +351,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
cdp.CustomControl.Setup(this, i, _canReorderItems);
|
||||
var itemLayout = cdp.VerticalPanel();
|
||||
cdp.CustomControl.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor);
|
||||
if (_readOnly && itemLayout.Children.Count > 0)
|
||||
GenericEditor.OnReadOnlyProperty(itemLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
_elementsCount = size;
|
||||
|
||||
// Add/Remove buttons
|
||||
if (_canResize)
|
||||
if (_canResize && !_readOnly)
|
||||
{
|
||||
var panel = dragArea.HorizontalPanel();
|
||||
panel.Panel.Size = new Float2(0, 20);
|
||||
@@ -585,7 +598,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 +607,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_dragHandlers.OnDragLeave();
|
||||
_dragHandlers?.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
if (button == MouseButton.Left && _editor._canEditKeys)
|
||||
{
|
||||
OnEditClicked(null);
|
||||
return true;
|
||||
@@ -189,6 +189,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute);
|
||||
if (collection != null)
|
||||
{
|
||||
_canEditKeys &= collection.CanReorderItems;
|
||||
_readOnly = collection.ReadOnly;
|
||||
_notNullItems = collection.NotNullItems;
|
||||
if (collection.BackgroundColor.HasValue)
|
||||
@@ -197,6 +198,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
spacing = collection.Spacing;
|
||||
_displayType = collection.Display;
|
||||
}
|
||||
if (attributes != null && attributes.Any(x => x is ReadOnlyAttribute))
|
||||
{
|
||||
_readOnly = true;
|
||||
_canEditKeys = false;
|
||||
}
|
||||
|
||||
// Size
|
||||
if (layout.ContainerControl is DropPanel dropPanel)
|
||||
@@ -239,14 +245,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType<object>();
|
||||
var keys = keysEnumerable as object[] ?? keysEnumerable.ToArray();
|
||||
var valuesType = new ScriptType(valueType);
|
||||
|
||||
bool single = valuesType.IsPrimitive ||
|
||||
valuesType.Equals(new ScriptType(typeof(string))) ||
|
||||
valuesType.IsEnum ||
|
||||
(valuesType.GetFields().Length == 1 && valuesType.GetProperties().Length == 0) ||
|
||||
(valuesType.GetProperties().Length == 1 && valuesType.GetFields().Length == 0) ||
|
||||
valuesType.Equals(new ScriptType(typeof(JsonAsset))) ||
|
||||
valuesType.Equals(new ScriptType(typeof(SettingsBase)));
|
||||
|
||||
// Use separate layout cells for each collection items to improve layout updates for them in separation
|
||||
var useSharedLayout = valueType.IsPrimitive || valueType.IsEnum;
|
||||
@@ -263,6 +261,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var property = panel.AddPropertyItem(new DictionaryItemLabel(this, key));
|
||||
var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel();
|
||||
itemLayout.Object(new DictionaryValueContainer(valuesType, key, Values), overrideEditor);
|
||||
if (_readOnly && itemLayout.Children.Count > 0)
|
||||
GenericEditor.OnReadOnlyProperty(itemLayout);
|
||||
}
|
||||
}
|
||||
_elementsCount = size;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
internal sealed class DummyEditor : CustomEditor
|
||||
{
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
string valueName;
|
||||
if (Values.Count != 0 && Values[0] != null)
|
||||
valueName = Values[0].ToString();
|
||||
else
|
||||
valueName = "null";
|
||||
layout.Label($"{valueName} ({Values.Type})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -581,56 +581,51 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
return layout;
|
||||
}
|
||||
|
||||
internal static void OnReadOnlyProperty(LayoutElementsContainer itemLayout, int labelIndex = -1)
|
||||
{
|
||||
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 && labelIndex != -1)
|
||||
{
|
||||
list = list1;
|
||||
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
|
||||
}
|
||||
else if (control?.Control != null)
|
||||
{
|
||||
control.Control.Enabled = false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 +664,45 @@ 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)
|
||||
{
|
||||
OnReadOnlyProperty(itemLayout, labelIndex);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public sealed class StringEditor : CustomEditor
|
||||
{
|
||||
private TextBoxElement _element;
|
||||
private string _watermarkText;
|
||||
private Color _watermarkColor;
|
||||
private Color _defaultWatermarkColor;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
@@ -21,15 +24,26 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
bool isMultiLine = false;
|
||||
_watermarkText = string.Empty;
|
||||
|
||||
var attributes = Values.GetAttributes();
|
||||
var multiLine = attributes?.FirstOrDefault(x => x is MultilineTextAttribute);
|
||||
var watermarkAttribute = attributes?.FirstOrDefault(x => x is WatermarkAttribute);
|
||||
if (multiLine != null)
|
||||
{
|
||||
isMultiLine = true;
|
||||
}
|
||||
|
||||
_element = layout.TextBox(isMultiLine);
|
||||
_defaultWatermarkColor = _element.TextBox.WatermarkTextColor;
|
||||
if (watermarkAttribute is WatermarkAttribute watermark)
|
||||
{
|
||||
_watermarkText = watermark.WatermarkText;
|
||||
var watermarkColor = watermark.WatermarkColor > 0 ? Color.FromRGB(watermark.WatermarkColor) : FlaxEngine.GUI.Style.Current.ForegroundDisabled;
|
||||
_watermarkColor = watermarkColor;
|
||||
_element.TextBox.WatermarkText = watermark.WatermarkText;
|
||||
_element.TextBox.WatermarkTextColor = watermarkColor;
|
||||
}
|
||||
_element.TextBox.EditEnd += () => SetValue(_element.Text);
|
||||
}
|
||||
|
||||
@@ -41,12 +55,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
_element.TextBox.Text = string.Empty;
|
||||
_element.TextBox.WatermarkTextColor = _defaultWatermarkColor;
|
||||
_element.TextBox.WatermarkText = "Different values";
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.TextBox.Text = (string)Values[0];
|
||||
_element.TextBox.WatermarkText = string.Empty;
|
||||
_element.TextBox.WatermarkTextColor = _watermarkColor;
|
||||
_element.TextBox.WatermarkText = _watermarkText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
float namesWidth = _splitterValue * Width;
|
||||
int count = _element.Labels.Count;
|
||||
float[] yStarts = new float[count + 1];
|
||||
for (int i = 1; i < count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var label = _element.Labels[i];
|
||||
|
||||
@@ -251,9 +251,13 @@ namespace FlaxEditor.CustomEditors.GUI
|
||||
else if (_children.Count <= label.FirstChildControlIndex)
|
||||
yStarts[i] = y;
|
||||
else
|
||||
{
|
||||
yStarts[i] = _children[label.FirstChildControlIndex].Top;
|
||||
if (i == count - 1)
|
||||
yStarts[i + 1] = _children[label.FirstChildControlIndex].Bottom;
|
||||
}
|
||||
|
||||
}
|
||||
yStarts[count] = y;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var label = _element.Labels[i];
|
||||
|
||||
@@ -318,7 +318,7 @@ namespace FlaxEditor.CustomEditors
|
||||
if (header.FontSize > 0)
|
||||
element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize);
|
||||
if (header.Color > 0)
|
||||
element.Label.TextColor = Color.FromRGBA(header.Color);
|
||||
element.Label.TextColor = Color.FromRGB(header.Color);
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
Text = "+",
|
||||
Parent = this,
|
||||
TooltipText = "Save Color.",
|
||||
Tag = null,
|
||||
};
|
||||
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
|
||||
@@ -370,9 +371,25 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
|
||||
// Color difference
|
||||
var newRect = new Rectangle(_cOK.X, _cHex.Bottom + PickerMargin, _cCancel.Right - _cOK.Left, 0);
|
||||
newRect.Size.Y = _cValue.Bottom - newRect.Y;
|
||||
Render2D.FillRectangle(newRect, _value * _value.A);
|
||||
var newRect = new Rectangle(_cOK.X - 3, _cHex.Bottom + PickerMargin, 130, 0);
|
||||
newRect.Size.Y = 50;
|
||||
Render2D.FillRectangle(newRect, Color.White);
|
||||
var smallRectSize = 10;
|
||||
var numHor = Mathf.FloorToInt(newRect.Width / smallRectSize);
|
||||
var numVer = Mathf.FloorToInt(newRect.Height / smallRectSize);
|
||||
// Draw checkerboard for background of color to help with transparency
|
||||
for (int i = 0; i < numHor; i++)
|
||||
{
|
||||
for (int j = 0; j < numVer; j++)
|
||||
{
|
||||
if ((i + j) % 2 == 0 )
|
||||
{
|
||||
var rect = new Rectangle(newRect.X + smallRectSize * i, newRect.Y + smallRectSize * j, new Float2(smallRectSize));
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
Render2D.FillRectangle(newRect, _value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -498,6 +515,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
Text = "+",
|
||||
Parent = this,
|
||||
TooltipText = "Save Color.",
|
||||
Tag = null,
|
||||
};
|
||||
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
|
||||
|
||||
@@ -311,7 +311,9 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
// Alpha
|
||||
float alphaY = _slider2Rect.Height * (1 - _color.A);
|
||||
var alphaR = new Rectangle(_slider2Rect.X - slidersOffset, _slider2Rect.Y + alphaY - slidersThickness / 2, _slider2Rect.Width + slidersOffset * 2, slidersThickness);
|
||||
Render2D.FillRectangle(_slider2Rect, _color, _color, Color.Transparent, Color.Transparent);
|
||||
var color = _color;
|
||||
color.A = 1; // Keep slider 2 fill rect from changing color alpha while selecting.
|
||||
Render2D.FillRectangle(_slider2Rect, color, color, Color.Transparent, Color.Transparent);
|
||||
Render2D.DrawRectangle(_slider2Rect, _isMouseDownSlider2 ? style.BackgroundSelected : Color.Black);
|
||||
Render2D.DrawRectangle(alphaR, _isMouseDownSlider2 ? Color.White : Color.Gray);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.GUI.Drag;
|
||||
|
||||
/// <summary>
|
||||
/// Control type drag handler.
|
||||
/// </summary>
|
||||
public sealed class DragControlType : DragActorType<DragEventArgs>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DragControlType"/> class.
|
||||
/// </summary>
|
||||
/// <param name="validateFunction">The validation function</param>
|
||||
public DragControlType(Func<ScriptType, bool> validateFunction)
|
||||
: base(validateFunction)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for handling control type drag and drop (for spawning).
|
||||
/// </summary>
|
||||
/// <seealso cref="Control" />
|
||||
/// <seealso cref="ActorNode" />
|
||||
public class DragControlType<U> : DragHelper<ScriptType, U> where U : DragEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The default prefix for drag data used for actor type drag and drop.
|
||||
/// </summary>
|
||||
public const string DragPrefix = "CTYPE!?";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new DragHelper
|
||||
/// </summary>
|
||||
/// <param name="validateFunction">The validation function</param>
|
||||
public DragControlType(Func<ScriptType, bool> validateFunction)
|
||||
: base(validateFunction)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override DragData ToDragData(ScriptType item) => GetDragData(item);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override DragData ToDragData(IEnumerable<ScriptType> items)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drag data.
|
||||
/// </summary>
|
||||
/// <param name="item">The control type.</param>
|
||||
/// <returns>The data</returns>
|
||||
public static DragData GetDragData(Type item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException();
|
||||
return new DragDataText(DragPrefix + item.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drag data.
|
||||
/// </summary>
|
||||
/// <param name="item">The control type.</param>
|
||||
/// <returns>The data</returns>
|
||||
public static DragData GetDragData(ScriptType item)
|
||||
{
|
||||
if (item == ScriptType.Null)
|
||||
throw new ArgumentNullException();
|
||||
return new DragDataText(DragPrefix + item.TypeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse the drag data to extract <see cref="Type"/> collection.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>Gathered objects or empty array if cannot get any valid.</returns>
|
||||
public override IEnumerable<ScriptType> FromDragData(DragData data)
|
||||
{
|
||||
if (data is DragDataText dataText)
|
||||
{
|
||||
if (dataText.Text.StartsWith(DragPrefix))
|
||||
{
|
||||
// Remove prefix and parse spitted names
|
||||
var types = dataText.Text.Remove(0, DragPrefix.Length).Split('\n');
|
||||
var results = new List<ScriptType>(types.Length);
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
{
|
||||
// Find type
|
||||
var obj = TypeUtils.GetType(types[i]);
|
||||
if (obj)
|
||||
results.Add(obj);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
return Utils.GetEmptyArray<ScriptType>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,9 +418,19 @@ namespace FlaxEditor.GUI.Tabs
|
||||
{
|
||||
// If scroll bar is visible it covers part of the tab header so include this in tab size to improve usability
|
||||
if (_orientation == Orientation.Horizontal && TabsPanel.HScrollBar.Visible)
|
||||
{
|
||||
tabsSize.Y += TabsPanel.HScrollBar.Height;
|
||||
var style = Style.Current;
|
||||
TabsPanel.HScrollBar.TrackColor = style.Background;
|
||||
TabsPanel.HScrollBar.ThumbColor = style.ForegroundGrey;
|
||||
}
|
||||
else if (_orientation == Orientation.Vertical && TabsPanel.VScrollBar.Visible)
|
||||
{
|
||||
tabsSize.X += TabsPanel.VScrollBar.Width;
|
||||
var style = Style.Current;
|
||||
TabsPanel.VScrollBar.TrackColor = style.Background;
|
||||
TabsPanel.VScrollBar.ThumbColor = style.ForegroundGrey;
|
||||
}
|
||||
}
|
||||
|
||||
// Fit the tabs panel
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// The keyframes.
|
||||
/// </summary>
|
||||
[EditorDisplay("Keyframes", EditorDisplayAttribute.InlineStyle), ExpandGroups]
|
||||
[Collection(CanReorderItems = false, ReadOnly = true)]
|
||||
[Collection(CanReorderItems = false, CanResize = true)]
|
||||
public List<KeyValuePair<string, object>> Keyframes;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// The parameters values.
|
||||
/// </summary>
|
||||
[EditorDisplay("Parameters", EditorDisplayAttribute.InlineStyle), ExpandGroups]
|
||||
[Collection(CanReorderItems = false, ReadOnly = true)]
|
||||
[Collection(CanReorderItems = false, CanResize = true)]
|
||||
public object[] Parameters;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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)
|
||||
@@ -696,6 +698,38 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
}
|
||||
|
||||
// Show tree guide lines
|
||||
if (Editor.Instance.Options.Options.Interface.ShowTreeLines)
|
||||
{
|
||||
TreeNode parentNode = Parent as TreeNode;
|
||||
bool thisNodeIsLast = false;
|
||||
while (parentNode != null && parentNode != ParentTree.Children[0])
|
||||
{
|
||||
float bottomOffset = 0;
|
||||
float topOffset = 0;
|
||||
|
||||
if (Parent == parentNode && this == Parent.Children[0])
|
||||
topOffset = 2;
|
||||
|
||||
if (thisNodeIsLast && parentNode.Children.Count == 1)
|
||||
bottomOffset = topOffset != 0 ? 4 : 2;
|
||||
|
||||
if (Parent == parentNode && this == Parent.Children[Parent.Children.Count - 1] && !_opened)
|
||||
{
|
||||
thisNodeIsLast = true;
|
||||
bottomOffset = topOffset != 0 ? 4 : 2;
|
||||
}
|
||||
|
||||
float leftOffset = 9;
|
||||
// Adjust offset for icon image
|
||||
if (_iconCollaped.IsValid)
|
||||
leftOffset += 18;
|
||||
var lineRect1 = new Rectangle(parentNode.TextRect.Left - leftOffset, parentNode.HeaderRect.Top + topOffset, 1, parentNode.HeaderRect.Height - bottomOffset);
|
||||
Render2D.FillRectangle(lineRect1, isSelected ? style.ForegroundGrey : style.LightBackground);
|
||||
parentNode = parentNode.Parent as TreeNode;
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
if (_opened)
|
||||
{
|
||||
@@ -712,6 +746,72 @@ 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) / _headerHeight) + 1, 0, children.Count - 1);
|
||||
if (GetChildGlobalRectangle(children[firstVisibleChild], ref globalTransform).Top > globalClipping.Top || !children[firstVisibleChild].Visible)
|
||||
{
|
||||
// Estimate overshoot, either it's partially visible or hidden in the tree
|
||||
for (; firstVisibleChild > 0; firstVisibleChild--)
|
||||
{
|
||||
var child = children[firstVisibleChild];
|
||||
if (!child.Visible)
|
||||
continue;
|
||||
|
||||
if (GetChildGlobalRectangle(child, ref globalTransform).Top < globalClipping.Top)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = firstVisibleChild; i < children.Count; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
if (!child.Visible)
|
||||
continue;
|
||||
|
||||
var childGlobalRect = GetChildGlobalRectangle(child, ref globalTransform);
|
||||
if (!globalClipping.Intersects(ref childGlobalRect))
|
||||
break;
|
||||
|
||||
Render2D.PushTransform(ref child._cachedTransform);
|
||||
child.Draw();
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
|
||||
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 +1096,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 +1204,6 @@ namespace FlaxEditor.GUI.Tree
|
||||
{
|
||||
// TODO: perform layout for any non-TreeNode controls
|
||||
_cachedHeight = _headerHeight;
|
||||
_cachedTextColor = CacheTextColor();
|
||||
Size = new Float2(width, _headerHeight);
|
||||
}
|
||||
|
||||
@@ -1154,7 +1253,6 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
|
||||
_cachedHeight = height;
|
||||
_cachedTextColor = CacheTextColor();
|
||||
Height = Mathf.Max(_headerHeight, y);
|
||||
}
|
||||
|
||||
|
||||
@@ -253,7 +253,11 @@ namespace FlaxEditor
|
||||
{
|
||||
// Select node (with additive mode)
|
||||
var selection = new List<SceneGraphNode>();
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
if (Root.GetKey(KeyboardKeys.Shift) && transformGizmo.Selection.Contains(uiControlNode))
|
||||
{
|
||||
// Move whole selection
|
||||
}
|
||||
else if (Root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
// Add/remove from selection
|
||||
selection.AddRange(transformGizmo.Selection);
|
||||
@@ -261,13 +265,14 @@ namespace FlaxEditor
|
||||
selection.Remove(uiControlNode);
|
||||
else
|
||||
selection.Add(uiControlNode);
|
||||
owner.Select(selection);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select
|
||||
selection.Add(uiControlNode);
|
||||
owner.Select(selection);
|
||||
}
|
||||
owner.Select(selection);
|
||||
|
||||
// Initialize control movement
|
||||
_mouseMovesControl = true;
|
||||
@@ -499,6 +504,15 @@ namespace FlaxEditor
|
||||
bool drawAnySelectedControl = false;
|
||||
var transformGizmo = TransformGizmo;
|
||||
var mousePos = PointFromWindow(RootWindow.MousePosition);
|
||||
if (EnableSelecting && !_mouseMovesControl && !_mouseMovesWidget && IsMouseOver)
|
||||
{
|
||||
// Highlight control under mouse for easier selecting (except if already selected)
|
||||
if (RayCastControl(ref mousePos, out var hitControl) &&
|
||||
(transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl)))
|
||||
{
|
||||
DrawControl(null, hitControl, false, ref mousePos, ref drawAnySelectedControl);
|
||||
}
|
||||
}
|
||||
if (transformGizmo != null)
|
||||
{
|
||||
// Selected UI controls outline
|
||||
@@ -511,15 +525,6 @@ namespace FlaxEditor
|
||||
}
|
||||
}
|
||||
}
|
||||
if (EnableSelecting && !_mouseMovesControl && !_mouseMovesWidget && IsMouseOver)
|
||||
{
|
||||
// Highlight control under mouse for easier selecting (except if already selected)
|
||||
if (RayCastControl(ref mousePos, out var hitControl) &&
|
||||
(transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl)))
|
||||
{
|
||||
DrawControl(null, hitControl, false, ref mousePos, ref drawAnySelectedControl);
|
||||
}
|
||||
}
|
||||
if (drawAnySelectedControl)
|
||||
Render2D.PopTransform();
|
||||
|
||||
@@ -617,40 +622,39 @@ namespace FlaxEditor
|
||||
// Draw sizing widgets
|
||||
if (_widgets == null)
|
||||
_widgets = new List<Widget>();
|
||||
var widgetSize = 8.0f;
|
||||
var widgetSize = 10.0f;
|
||||
var viewScale = ViewScale;
|
||||
if (viewScale < 0.7f)
|
||||
widgetSize *= viewScale;
|
||||
var controlSize = control.Size.Absolute.MinValue / 50.0f;
|
||||
if (controlSize < 1.0f)
|
||||
widgetSize *= Mathf.Clamp(controlSize + 0.1f, 0.1f, 1.0f);
|
||||
var cornerSize = new Float2(widgetSize);
|
||||
DrawControlWidget(uiControl, ref ul, ref mousePos, ref cornerSize, new Float2(-1, -1), CursorType.SizeNWSE);
|
||||
DrawControlWidget(uiControl, ref ur, ref mousePos, ref cornerSize, new Float2(1, -1), CursorType.SizeNESW);
|
||||
DrawControlWidget(uiControl, ref bl, ref mousePos, ref cornerSize, new Float2(-1, 1), CursorType.SizeNESW);
|
||||
DrawControlWidget(uiControl, ref br, ref mousePos, ref cornerSize, new Float2(1, 1), CursorType.SizeNWSE);
|
||||
var edgeSizeV = new Float2(widgetSize * 2, widgetSize);
|
||||
var edgeSizeH = new Float2(edgeSizeV.Y, edgeSizeV.X);
|
||||
var widgetHandleSize = new Float2(widgetSize);
|
||||
DrawControlWidget(uiControl, ref ul, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, -1), CursorType.SizeNWSE);
|
||||
DrawControlWidget(uiControl, ref ur, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, -1), CursorType.SizeNESW);
|
||||
DrawControlWidget(uiControl, ref bl, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, 1), CursorType.SizeNESW);
|
||||
DrawControlWidget(uiControl, ref br, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, 1), CursorType.SizeNWSE);
|
||||
Float2.Lerp(ref ul, ref bl, 0.5f, out var el);
|
||||
Float2.Lerp(ref ur, ref br, 0.5f, out var er);
|
||||
Float2.Lerp(ref ul, ref ur, 0.5f, out var eu);
|
||||
Float2.Lerp(ref bl, ref br, 0.5f, out var eb);
|
||||
DrawControlWidget(uiControl, ref el, ref mousePos, ref edgeSizeH, new Float2(-1, 0), CursorType.SizeWE);
|
||||
DrawControlWidget(uiControl, ref er, ref mousePos, ref edgeSizeH, new Float2(1, 0), CursorType.SizeWE);
|
||||
DrawControlWidget(uiControl, ref eu, ref mousePos, ref edgeSizeV, new Float2(0, -1), CursorType.SizeNS);
|
||||
DrawControlWidget(uiControl, ref eb, ref mousePos, ref edgeSizeV, new Float2(0, 1), CursorType.SizeNS);
|
||||
DrawControlWidget(uiControl, ref el, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, 0), CursorType.SizeWE);
|
||||
DrawControlWidget(uiControl, ref er, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, 0), CursorType.SizeWE);
|
||||
DrawControlWidget(uiControl, ref eu, ref mousePos, ref widgetHandleSize, viewScale, new Float2(0, -1), CursorType.SizeNS);
|
||||
DrawControlWidget(uiControl, ref eb, ref mousePos, ref widgetHandleSize, viewScale, new Float2(0, 1), CursorType.SizeNS);
|
||||
|
||||
// TODO: draw anchors
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, Float2 resizeAxis, CursorType cursor)
|
||||
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size,float scale, Float2 resizeAxis, CursorType cursor)
|
||||
{
|
||||
var style = Style.Current;
|
||||
var rect = new Rectangle(pos - size * 0.5f, size);
|
||||
var rect = new Rectangle((pos + resizeAxis * 10 * scale) - size * 0.5f, size);
|
||||
if (rect.Contains(ref mousePos))
|
||||
{
|
||||
Render2D.FillRectangle(rect, style.Foreground);
|
||||
Render2D.DrawRectangle(rect, style.SelectionBorder);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -202,6 +202,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
var prefabId = ((ActorNode)selection[0]).Actor.PrefabID;
|
||||
var prefab = FlaxEngine.Content.LoadAsync<Prefab>(prefabId);
|
||||
Editor.Windows.ContentWin.ClearItemsSearch();
|
||||
Editor.Windows.ContentWin.Select(prefab);
|
||||
}
|
||||
|
||||
@@ -227,7 +228,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
|
||||
|
||||
@@ -565,6 +565,7 @@ namespace FlaxEditor.Modules
|
||||
if (item != null)
|
||||
Editor.ContentEditing.Open(item);
|
||||
});
|
||||
cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
|
||||
// Scene
|
||||
MenuScene = MainMenu.AddButton("Scene");
|
||||
@@ -619,7 +620,6 @@ namespace FlaxEditor.Modules
|
||||
_menuToolsTakeScreenshot = cm.AddButton("Take screenshot", inputOptions.TakeScreenshot, Editor.Windows.TakeScreenshot);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Plugins", () => Editor.Windows.PluginsWin.Show());
|
||||
cm.AddButton("Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
|
||||
// Window
|
||||
MenuWindow = MainMenu.AddButton("Window");
|
||||
@@ -640,7 +640,7 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Visual Script Debugger", Editor.Windows.VisualScriptDebuggerWin.FocusOrShow);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Save window layout", Editor.Windows.SaveLayout);
|
||||
_menuWindowApplyWindowLayout = cm.AddChildMenu("Apply window layout");
|
||||
_menuWindowApplyWindowLayout = cm.AddChildMenu("Window layouts");
|
||||
cm.AddButton("Restore default layout", Editor.Windows.LoadDefaultLayout);
|
||||
|
||||
// Help
|
||||
@@ -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
|
||||
|
||||
@@ -210,6 +210,13 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Interface"), EditorOrder(310)]
|
||||
public bool SeparateValueAndUnit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the option to put a space between numbers and units for unit formatting.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Interface"), EditorOrder(320)]
|
||||
public bool ShowTreeLines { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamps prefix mode for output log messages.
|
||||
/// </summary>
|
||||
|
||||
@@ -255,6 +255,17 @@ namespace FlaxEditor.Options
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure custom fonts are valid, reset if not
|
||||
var defaultInterfaceOptions = new InterfaceOptions();
|
||||
if (Style.Current.FontTitle == null)
|
||||
Style.Current.FontTitle = defaultInterfaceOptions.TitleFont.GetFont();
|
||||
if (Style.Current.FontSmall == null)
|
||||
Style.Current.FontSmall = defaultInterfaceOptions.SmallFont.GetFont();
|
||||
if (Style.Current.FontMedium == null)
|
||||
Style.Current.FontMedium = defaultInterfaceOptions.MediumFont.GetFont();
|
||||
if (Style.Current.FontLarge == null)
|
||||
Style.Current.FontLarge = defaultInterfaceOptions.LargeFont.GetFont();
|
||||
|
||||
// Set fallback fonts
|
||||
var fallbackFonts = Options.Interface.FallbackFonts;
|
||||
if (fallbackFonts == null || fallbackFonts.Length == 0 || fallbackFonts.All(x => x == null))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -15,6 +15,7 @@ using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
@@ -29,6 +30,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
private DragScripts _dragScripts;
|
||||
private DragAssets _dragAssets;
|
||||
private DragActorType _dragActorType;
|
||||
private DragControlType _dragControlType;
|
||||
private DragScriptItems _dragScriptItems;
|
||||
private DragHandlers _dragHandlers;
|
||||
private List<Rectangle> _highlights;
|
||||
@@ -68,7 +70,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 +99,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 +293,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;
|
||||
@@ -354,7 +356,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
private void OnRenamed(RenamePopup renamePopup)
|
||||
{
|
||||
using (new UndoBlock(ActorNode.Root.Undo, Actor, "Rename"))
|
||||
Actor.Name = renamePopup.Text;
|
||||
Actor.Name = renamePopup.Text.Trim();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -366,7 +368,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);
|
||||
}
|
||||
}
|
||||
@@ -439,6 +441,17 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
}
|
||||
if (_dragActorType.OnDragEnter(data))
|
||||
return _dragActorType.Effect;
|
||||
|
||||
// Check if drag control type
|
||||
if (_dragControlType == null)
|
||||
{
|
||||
_dragControlType = new DragControlType(ValidateDragControlType);
|
||||
_dragHandlers.Add(_dragControlType);
|
||||
}
|
||||
if (_dragControlType.OnDragEnter(data))
|
||||
return _dragControlType.Effect;
|
||||
|
||||
// Check if drag script item
|
||||
if (_dragScriptItems == null)
|
||||
{
|
||||
_dragScriptItems = new DragScriptItems(ValidateDragScriptItem);
|
||||
@@ -572,10 +585,33 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
Editor.LogWarning("Failed to spawn actor of type " + item.TypeName);
|
||||
continue;
|
||||
}
|
||||
actor.StaticFlags = Actor.StaticFlags;
|
||||
actor.StaticFlags = newParent.StaticFlags;
|
||||
actor.Name = item.Name;
|
||||
actor.Transform = Actor.Transform;
|
||||
ActorNode.Root.Spawn(actor, Actor);
|
||||
ActorNode.Root.Spawn(actor, newParent);
|
||||
actor.OrderInParent = newOrder;
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
// Drag control type
|
||||
else if (_dragControlType != null && _dragControlType.HasValidDrag)
|
||||
{
|
||||
for (int i = 0; i < _dragControlType.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragControlType.Objects[i];
|
||||
var control = item.CreateInstance() as Control;
|
||||
if (control == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName);
|
||||
continue;
|
||||
}
|
||||
var uiControl = new UIControl
|
||||
{
|
||||
Control = control,
|
||||
StaticFlags = newParent.StaticFlags,
|
||||
Name = item.Name,
|
||||
};
|
||||
ActorNode.Root.Spawn(uiControl, newParent);
|
||||
uiControl.OrderInParent = newOrder;
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
@@ -590,6 +626,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
var item = _dragScriptItems.Objects[i];
|
||||
var actorType = Editor.Instance.CodeEditing.Actors.Get(item);
|
||||
var scriptType = Editor.Instance.CodeEditing.Scripts.Get(item);
|
||||
if (actorType != ScriptType.Null)
|
||||
{
|
||||
var actor = actorType.CreateInstance() as Actor;
|
||||
@@ -604,6 +641,18 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
ActorNode.Root.Spawn(actor, spawnParent);
|
||||
actor.OrderInParent = newOrder;
|
||||
}
|
||||
else if (scriptType != ScriptType.Null)
|
||||
{
|
||||
if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn script of type " + actorType.TypeName);
|
||||
continue;
|
||||
}
|
||||
IUndoAction action = new AddRemoveScript(true, newParent, scriptType);
|
||||
Select();
|
||||
ActorNode.Root.Undo?.AddAction(action);
|
||||
action.Do();
|
||||
}
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
@@ -640,8 +689,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;
|
||||
|
||||
@@ -656,12 +705,17 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return true;
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
private static bool ValidateDragControlType(ScriptType controlType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
|
||||
}
|
||||
|
||||
private bool ValidateDragScriptItem(ScriptItem script)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null || Editor.Instance.CodeEditing.Scripts.Get(script) != ScriptType.Null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -704,6 +758,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
_dragScripts = null;
|
||||
_dragAssets = null;
|
||||
_dragActorType = null;
|
||||
_dragControlType = null;
|
||||
_dragScriptItems = null;
|
||||
_dragHandlers?.Clear();
|
||||
_dragHandlers = null;
|
||||
|
||||
@@ -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();
|
||||
@@ -142,6 +186,18 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnEndMouseCapture();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.Delete:
|
||||
_editor.SetAsset(_index, Guid.Empty);
|
||||
return true;
|
||||
}
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -149,40 +205,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 +395,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 +423,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 +442,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 +478,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 +570,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 +599,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 +628,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 +669,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 +677,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 +697,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 +717,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 +789,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 +806,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 +826,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 +866,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 +893,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 +912,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 +933,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 +963,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 +996,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 +1032,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
|
||||
{
|
||||
|
||||
@@ -430,7 +430,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Smoothstep",
|
||||
Description = "Returns a smooth Hermite interpolation between 0 and 1, if value is in the range [min, max].",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(120, 60),
|
||||
Size = new Float2(200, 60),
|
||||
ConnectionsHints = ConnectionsHint.Numeric,
|
||||
IndependentBoxes = new[] { 0, 1, 2 },
|
||||
DependentBoxes = new[] { 3 },
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -66,6 +66,8 @@ namespace FlaxEditor.Surface.Undo
|
||||
// Initialize
|
||||
if (node.Values != null && node.Values.Length == _nodeValues.Length)
|
||||
Array.Copy(_nodeValues, node.Values, _nodeValues.Length);
|
||||
else if (_nodeValues != null && (node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
|
||||
node.Values = (object[])_nodeValues.Clone();
|
||||
else if (_nodeValues != null && _nodeValues.Length != 0)
|
||||
throw new InvalidOperationException("Invalid node values.");
|
||||
node.Location = _nodeLocation;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace FlaxEditor.Tools.Foliage
|
||||
}
|
||||
}
|
||||
|
||||
[EditorOrder(20), EditorDisplay("Model"), Collection(ReadOnly = true), Tooltip("Model materials override collection. Can be used to change a specific material of the mesh to the custom one without editing the asset.")]
|
||||
[EditorOrder(20), EditorDisplay("Model"), Collection(CanResize = true), Tooltip("Model materials override collection. Can be used to change a specific material of the mesh to the custom one without editing the asset.")]
|
||||
public MaterialBase[] Materials
|
||||
{
|
||||
get
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user