Compare commits

..

166 Commits

Author SHA1 Message Date
mafiesto4 c5520f2777 Update version 2024-04-26 19:12:40 +02:00
mafiesto4 4414398f09 Fix crash when accessing physics objects state in OnLateFixedUpdate
#2494
2024-04-26 19:08:25 +02:00
mafiesto4 5e5497ff18 Add Tags.Find utility
#2492
2024-04-26 19:06:39 +02:00
mafiesto4 aaadf3065d Use ARM64 on the latest macOS builds 2024-04-25 19:41:38 +02:00
mafiesto4 b134a5567d Format comments 2024-04-25 19:30:12 +02:00
mafiesto4 740a31b7a9 Merge branch 'duarteroso-bug/issue-2476' 2024-04-25 19:27:23 +02:00
mafiesto4 75e25bf1f7 Merge branch 'bug/issue-2476' of https://github.com/duarteroso/FlaxEngine into duarteroso-bug/issue-2476 2024-04-25 19:27:11 +02:00
mafiesto4 279c80a9ae Merge branch 'duarteroso-bug/issue-2477' 2024-04-25 19:25:55 +02:00
mafiesto4 4dbfc01715 Merge branch 'bug/issue-2477' of https://github.com/duarteroso/FlaxEngine into duarteroso-bug/issue-2477 2024-04-25 19:25:50 +02:00
mafiesto4 45706be3a6 Merge branch 'duarteroso-bug/issue-2475' 2024-04-25 19:25:29 +02:00
mafiesto4 723423a3fa Merge branch 'bug/issue-2475' of https://github.com/duarteroso/FlaxEngine into duarteroso-bug/issue-2475 2024-04-25 18:02:46 +02:00
mafiesto4 42de657629 Merge branch 'GoaLitiuM-release_prefab_fix' 2024-04-25 18:02:02 +02:00
mafiesto4 16f1e8a3e1 Merge branch 'release_prefab_fix' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-release_prefab_fix 2024-04-25 18:01:57 +02:00
mafiesto4 2c1b2d2b7c Optimize ActorTreeNode reparent when it's not collapsed but one of the parents is 2024-04-25 18:01:06 +02:00
mafiesto4 19cc33b200 Merge branch 'GoaLitiuM-treenode_optimize' 2024-04-25 18:00:27 +02:00
mafiesto4 3641e156ee Merge branch 'treenode_optimize' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-treenode_optimize 2024-04-25 17:18:54 +02:00
mafiesto4 4413af5d20 Merge branch 'GoaLitiuM-modelprefab_freeze_fix' 2024-04-25 17:17:44 +02:00
GoaLitiuM 8c2e7bccaa Delete Prefab default instance when scripting unloads 2024-04-23 22:14:06 +03:00
GoaLitiuM 3e7ac04a88 Fix freeze when selecting ModelPrefab referencing a removed asset 2024-04-23 20:55:20 +03:00
Duarte Roso 9bc9d95a22 Revert "Add android settings"
This reverts commit b6692b4747.
2024-04-23 16:10:08 +02:00
Duarte Roso b6e36c0014 Add android settings
Added:
- version code
- minimum Sdk version
- target Sdk version
2024-04-23 16:09:10 +02:00
Duarte Roso b6692b4747 Add android settings
Added:
- version code
- minimum Sdk version
- target Sdk version
2024-04-23 15:58:36 +02:00
Duarte Roso 62c2e3b8f0 Add exported property to the activity element 2024-04-23 15:30:36 +02:00
Duarte Roso d4774a2bfe Change screen orientation enum 2024-04-23 15:13:04 +02:00
mafiesto4 705856da24 Fix crash when resizing window on Vulkan
Regression from 8488a8d387
#2356
2024-04-23 10:08:16 +02:00
mafiesto4 ab9cc16529 Fix EyeAdaptation bug to flash on play mode start in Editor when time gets reset 2024-04-23 00:03:21 +02:00
mafiesto4 203f03a597 Add Write/Read methods to NetworkStream for INetworkSerializable sending in C# api 2024-04-22 23:25:19 +02:00
mafiesto4 4fbe210730 Fix heightfield not saved when editing splatmap of terrain with physical materials in use
#2262
2024-04-22 23:20:44 +02:00
mafiesto4 6eb431d12c Fix rare error on drag&drop in Editor 2024-04-22 23:17:56 +02:00
mafiesto4 890569ea3b Add logging and cleaning up leaked scene objects after play mode in Editor 2024-04-22 22:59:38 +02:00
mafiesto4 d8a1de64d1 Fix surface node moving regression from f19977a956
#2466
2024-04-22 22:56:01 +02:00
mafiesto4 b92fbcb3bc Add more const correctness
#2467
2024-04-22 22:53:27 +02:00
mafiesto4 515ee96a31 Fix compilation regression 2024-04-22 19:10:06 +02:00
mafiesto4 f19977a956 Fix surface node context menu to not show when moving surface
#2466
2024-04-22 19:03:40 +02:00
mafiesto4 96f628429c Improve const correctness in Mesh and SkinnedMesh api
#2467i
2024-04-22 18:56:54 +02:00
mafiesto4 3ecbbcd0a0 Update Newtonsoft.Json 2024-04-22 18:44:50 +02:00
mafiesto4 a01495cde1 Merge similar source chunks
#2468
2024-04-22 18:36:25 +02:00
mafiesto4 b7dc0dd004 Fix crash when removing Anim Event visual script that is used in opened Animation timeline
#2471
2024-04-22 18:11:25 +02:00
mafiesto4 32b15f90ab Minor improvements 2024-04-22 18:10:58 +02:00
mafiesto4 e795a8b037 Fix compile warning 2024-04-22 15:29:25 +02:00
mafiesto4 e551eae30d Update engine assets 2024-04-22 13:32:09 +02:00
mafiesto4 5f02b4173a Fix nested animations sampling
#2472
2024-04-22 13:29:29 +02:00
mafiesto4 568a69081d Fix animated model skinning precision issues
#2460
2024-04-22 13:18:52 +02:00
GoaLitiuM 4dcdd8b5f7 Add Actor.HasScene and Script.HasScene
Useful in managed code to check the existence of the scene in hot paths
by avoiding expensive marshalling of the Scene object.
2024-04-20 20:58:55 +03:00
mafiesto4 1072b90c5b Fix blend shapes normals usage
#2459
2024-04-20 16:52:07 +02:00
mafiesto4 6aacea99ab Fix blend shapes transformation applying
#2459
2024-04-20 16:16:01 +02:00
mafiesto4 5e086809ae Fix crash on prefab preview with lightmap in use
#2454
2024-04-20 15:51:20 +02:00
GoaLitiuM 361e9a2929 Remove TreeNode text color caching
This is actually slower than just resolving the color before draw.
2024-04-20 16:26:29 +03:00
GoaLitiuM b25ee23b14 Optimize TreeNode rendering 2024-04-20 16:26:25 +03:00
mafiesto4 41ffc16b66 Fix FindRandomPointAroundCircle to always find a valid point on a NavMesh in the radius
#2398
2024-04-20 15:01:27 +02:00
mafiesto4 560cf65121 Add add/remove buttons to Multi Blend 2024-04-19 17:50:14 +02:00
mafiesto4 93e26afa05 Optimize Anim Graph node size and remove limit of max 64 state transitions 2024-04-19 16:31:11 +02:00
mafiesto4 7653fba381 Refactor Multi Blend nodes to support up to 255 blend points 2024-04-19 16:30:34 +02:00
mafiesto4 71fe280464 Refactor Visject Graph nodes data for unlimited size 2024-04-19 12:22:04 +02:00
mafiesto4 0845866c4d Add live debugging of blend position in Multi Blend 2024-04-18 18:42:31 +02:00
mafiesto4 a3b5f4b789 Add grid labels drawing to Multi Blend 2024-04-18 18:22:19 +02:00
mafiesto4 4b6b24f4c5 Fix precision of blend points moving in Multi Blend 2024-04-18 17:22:37 +02:00
mafiesto4 d8079367fd Add improved visuals of points in Multi Blend 2024-04-18 17:22:21 +02:00
mafiesto4 83b9e6e32a Add Ctrl to snap points to grid in Multi Blend editor 2024-04-18 17:04:23 +02:00
mafiesto4 f8f1c02338 Add context menu to Multi Blend node points 2024-04-18 15:27:23 +02:00
mafiesto4 285710360c Fix margins issues in context menus 2024-04-18 15:26:52 +02:00
mafiesto4 695c212cf0 Add tooltips to Multi Blend points 2024-04-18 13:10:33 +02:00
mafiesto4 05278ca418 Add undo for Multi Blend points moving
#1980
2024-04-17 23:28:10 +02:00
mafiesto4 e31ce787aa Add improved size of Multi Blend nodes
#1980
2024-04-17 23:04:09 +02:00
mafiesto4 d379b4c046 Fix missing UI Control preview when changing UI Widget type 2024-04-17 16:43:53 +02:00
mafiesto4 261faad93e Fix incorrect View Size for 2d character font material 2024-04-17 16:41:19 +02:00
mafiesto4 16554fe742 Fix cloning value utility for object references
#2399
2024-04-17 16:17:39 +02:00
mafiesto4 9f983cff49 Refactor engine loop to have better sync between game update, physics and drawing 2024-04-17 13:38:06 +02:00
mafiesto4 e53ab10145 Add Engine::UpdateCount to sync gameplay logic updates with game loop rather than draw frames 2024-04-17 13:31:12 +02:00
mafiesto4 692a61c948 Add Time.Synchronize to stabilize engine loop 2024-04-17 13:01:58 +02:00
mafiesto4 c83b74c85d Fix blending nested animations to properly handle it per-node
#2416
2024-04-16 23:25:37 +02:00
mafiesto4 171fc276fb Fix bug in ValueContainer.HasDifferentTypes causing incorrect editor setup
#2436
2024-04-16 22:32:10 +02:00
mafiesto4 446c1edafc Fix deselecting actors when using camera orbiting in viewport after releasing LMB when Alt is up
#2447
2024-04-16 21:00:54 +02:00
mafiesto4 03b498546d Refactor UpdateGraph to run after engine services and game scripts
#2413
2024-04-16 20:27:31 +02:00
mafiesto4 794b007170 Optimize draw calls sorting in opaque passes 2024-04-16 17:18:18 +02:00
mafiesto4 26c2b33fc0 Fix large world compile 2024-04-16 16:34:38 +02:00
mafiesto4 1a87e5a2ca Add support for importing .exr textures
#2375
2024-04-16 15:19:33 +02:00
mafiesto4 daf3671233 Fix model tool importing to use temp file only for Assimp 2024-04-16 14:38:12 +02:00
mafiesto4 5fd808af19 Update DirectXTex to mar2024 2024-04-16 13:40:42 +02:00
mafiesto4 ce67c3a98d Add ShowDebugDrawSkeleton to preview Animated Model skeleton via Debug Draw
#2443
2024-04-16 12:00:05 +02:00
mafiesto4 cd2f96e3c0 Add better anim events visuals in timeline editor
#2419
2024-04-16 10:17:30 +02:00
mafiesto4 9ad4997691 Add automatic restoring Anim Event tracks when reimporting animation asset
#2363
2024-04-15 19:27:28 +02:00
mafiesto4 37a02e3a7e Minor tweaks 2024-04-15 14:35:35 +02:00
mafiesto4 ed732a0189 Fix panning Visject Surface with middle mouse button (right button does it) to prevent missed connections removals
#2420
2024-04-15 14:34:53 +02:00
mafiesto4 00492a33a3 Fix crash when reimporting animation with translation set
#2445
2024-04-15 13:03:11 +02:00
mafiesto4 56d3b4f012 Fix deadlock when parsing invalid HTML text in RichTextBox
#2402
2024-04-15 12:44:42 +02:00
mafiesto4 85b9d93e91 Update to the latest ReSharper 2024-04-15 12:39:00 +02:00
mafiesto4 8584d51d9f Merge branch 'Tryibion-sort-search-toolbox' 2024-04-15 12:19:16 +02:00
mafiesto4 8714163ee4 Merge branch 'sort-search-toolbox' of https://github.com/Tryibion/FlaxEngine into Tryibion-sort-search-toolbox 2024-04-15 12:19:06 +02:00
mafiesto4 525c3a0f29 Fix terrain heightmap to use higher range format when decompressed
#2375
2024-04-15 12:16:50 +02:00
Tryibion 5acdff02cc Add sorting search actor toolbox 2024-04-14 20:39:15 -05:00
mafiesto4 0728637ce1 Fix using confirm via enter key in Create Terrain dialog 2024-04-15 00:17:21 +02:00
mafiesto4 9c3c4fbf54 Use uniform scale on new terrain to prevent artifacts in normal vectors 2024-04-15 00:13:54 +02:00
mafiesto4 d2508ad902 Merge branch 'Tryibion-actor-search' 2024-04-15 00:02:16 +02:00
mafiesto4 8a0cd7c30f Merge branch 'actor-search' of https://github.com/Tryibion/FlaxEngine into Tryibion-actor-search 2024-04-15 00:02:11 +02:00
mafiesto4 d729eb2b24 Fix crash when playing uninitialized audio source
#2441
2024-04-15 00:01:59 +02:00
mafiesto4 36ad821734 Fix 634b05fc16 2024-04-14 22:39:54 +02:00
Tryibion b66b85d5f4 Display all actors in Actor toolbox search tab when no filter is applied. 2024-04-14 14:03:51 -05:00
mafiesto4 778b967c61 Fix editor toolstrip by moving game cooking and building to the right for less missclicks
#2426
2024-04-14 15:05:25 +02:00
mafiesto4 3f6dd92a68 Fix crash when using old Blend with Mask node in Anim Graph
#2434
2024-04-14 14:51:40 +02:00
mafiesto4 80d7ac2581 Merge branch 'Muzz-IkFix' 2024-04-14 14:41:30 +02:00
mafiesto4 af91ce7f3f Merge branch 'IkFix' of https://github.com/Muzz/FlaxEngine into Muzz-IkFix 2024-04-14 14:39:57 +02:00
mafiesto4 5f7293d0a1 Merge branch 'Tryibion-lock-selection' 2024-04-14 14:34:26 +02:00
mafiesto4 5cdf1c5764 Merge branch 'lock-selection' of https://github.com/Tryibion/FlaxEngine into Tryibion-lock-selection 2024-04-14 14:34:10 +02:00
mafiesto4 e701cdbee5 Merge branch 'Menotdan-nav-mesh-init' 2024-04-14 14:31:25 +02:00
mafiesto4 dc45f46ff4 Merge branch 'nav-mesh-init' of https://github.com/Menotdan/FlaxEngine into Menotdan-nav-mesh-init 2024-04-14 14:31:20 +02:00
mafiesto4 634b05fc16 Fix code style 2024-04-14 13:31:32 +02:00
Menotdan db28afb591 Fix regression where if the navmesh was disabled on playmode beginning, the mesh would still exist in the backend. 2024-04-14 01:02:48 -04:00
Menotdan 84c1f6b5de Make NavMesh initialize earlier. 2024-04-14 00:58:11 -04:00
mafiesto4 53689063b9 Minor tweaks to Gameplay Globals
#2412
2024-04-14 00:09:29 +02:00
mafiesto4 c59e872ef2 Fix crash when loading invalid Variant structure 2024-04-14 00:09:01 +02:00
mafiesto4 2b95f11b1f Fixes to Vulkan backend 2024-04-13 23:55:39 +02:00
mafiesto4 fbfe77e386 Fix win32 compile 2024-04-13 21:44:16 +02:00
mafiesto4 60ccac31b5 Add more sanitizers 2024-04-13 19:20:49 +02:00
mafiesto4 d42e315e55 Fix atomics to accept constant 2024-04-13 19:20:49 +02:00
mafiesto4 9c2c02c1cf Fix various issues found with thread sanitizer on macOS 2024-04-13 19:20:49 +02:00
mafiesto4 8144db8e13 Fix various issues found with adrress sanitizer on macOS 2024-04-13 19:20:49 +02:00
mafiesto4 4ac6a292f7 Add option for using Address and Thread sanitizers 2024-04-13 19:20:49 +02:00
mafiesto4 3e475398e7 Optimize draw calls batching sorting with Merge Sort 2024-04-12 16:02:37 +02:00
Tryibion 81d4501868 Add locking selection in prefab window 2024-04-12 07:41:43 -05:00
Tryibion 1bc7455e09 Add locking scene selection in properties window. 2024-04-12 07:33:09 -05:00
mafiesto4 1e77f3aa5a Optimize DrawBatch for faster sorting 2024-04-12 13:46:20 +02:00
mafiesto4 e47e91c223 Merge branch 'Menotdan-hide-taa-settings' 2024-04-12 13:12:47 +02:00
mafiesto4 734f1ee4aa Improve doc comment 2024-04-12 13:09:24 +02:00
mafiesto4 6d38590ad4 Merge branch 'hide-taa-settings' of https://github.com/Menotdan/FlaxEngine into Menotdan-hide-taa-settings 2024-04-12 13:08:26 +02:00
mafiesto4 76becec094 Merge branch 'GoaLitiuM-dotnet_daily_runtime_fix' 2024-04-12 13:05:46 +02:00
mafiesto4 e7a6f39a72 Merge branch 'dotnet_daily_runtime_fix' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-dotnet_daily_runtime_fix 2024-04-12 13:05:40 +02:00
mafiesto4 98c5cc2d0f Merge branch 'Menotdan-particle-clone-fix' 2024-04-12 13:03:21 +02:00
mafiesto4 c8ad3e3a51 Merge branch 'particle-clone-fix' of https://github.com/Menotdan/FlaxEngine into Menotdan-particle-clone-fix 2024-04-12 13:03:16 +02:00
mafiesto4 5b25aeda32 Merge branch 'Tryibion-underline-fix' 2024-04-12 13:01:18 +02:00
mafiesto4 e5d700692f Merge branch 'underline-fix' of https://github.com/Tryibion/FlaxEngine into Tryibion-underline-fix 2024-04-12 13:01:10 +02:00
mafiesto4 495378c94d Merge branch 'GoaLitiuM-editor_viewport_camera_scaling' 2024-04-12 12:59:54 +02:00
mafiesto4 bf9701e132 Merge branch 'editor_viewport_camera_scaling' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-editor_viewport_camera_scaling 2024-04-12 12:56:22 +02:00
mafiesto4 14881494b8 Merge branch 'GoaLitiuM-debuglog_scroll_fix' 2024-04-12 12:55:17 +02:00
Menotdan c914e33462 Hide TAA settings if TAA isn't enabled. 2024-04-12 02:29:45 -04:00
Muzz 35f33b6746 Fixed IK to have correct bone roll 2024-04-12 12:07:26 +08:00
Menotdan e137d31839 Enforce Content:CloneAssetFile() running on the main thread, to avoid a bug that occurs when a particle emitter is created from one of the templates due to the creation coming from the content thread. 2024-04-11 17:37:31 -04:00
Tryibion 2e643347ec Fix selection height 2024-04-11 10:16:20 -05:00
Tryibion fb1685fe81 Fix underline being in wrong spot on different DPIs 2024-04-11 09:56:59 -05:00
GoaLitiuM 1ddf9ab0e1 Fix high mouse sensitivity in rotation gizmos 2024-04-09 21:45:28 +03:00
GoaLitiuM 8af8d50de1 Remove Editor viewport aspect ratio scaling from camera mouse movement
Rescaled the final mouse delta values to roughly matching default
viewport width in 1080p resolution.
2024-04-09 20:06:35 +03:00
GoaLitiuM e3093e0e09 Fix Debug Log scrolling when many entries are added at once 2024-04-09 20:05:58 +03:00
GoaLitiuM 02d5609f66 Fix .NET runtime validity checks with daily runtime builds 2024-04-09 20:05:37 +03:00
mafiesto4 b2f9da4113 Fix default diffuse color value when importing material 2024-04-05 16:54:10 +02:00
mafiesto4 a83b589e12 Fix error when applying prefab changes with Spline
#2384
2024-04-05 16:43:18 +02:00
mafiesto4 7578e1dbe3 Fix errors on using spline editor in Prefab window
#2383
2024-04-05 16:37:11 +02:00
mafiesto4 ae79f3ef0b Merge branch 'Zode-master' 2024-04-05 15:49:29 +02:00
mafiesto4 f164626c41 Merge branch 'master' of https://github.com/Zode/FlaxEngine into Zode-master 2024-04-05 15:49:20 +02:00
mafiesto4 354972fd9c Merge branch 'rkrahn-master' 2024-04-05 15:47:01 +02:00
mafiesto4 082733cb97 Merge branch 'master' of https://github.com/rkrahn/FlaxEngine into rkrahn-master 2024-04-05 15:46:30 +02:00
mafiesto4 97bd90d4be Change default collision type to ConvexMesh from TriangleMesh 2024-04-05 15:23:09 +02:00
mafiesto4 e788be46af Merge branch 'GoaLitiuM-win_dev_build_speed_fix' 2024-04-04 18:35:37 +02:00
mafiesto4 afd56974ef Merge branch 'win_dev_build_speed_fix' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-win_dev_build_speed_fix 2024-04-04 18:35:31 +02:00
mafiesto4 84e04de23d Add more improvements to optional tess/geo shaders 2024-04-04 18:35:26 +02:00
rkrahn 07c0b4567e Merge branch 'master' of https://github.com/rkrahn/FlaxEngine 2024-04-01 09:54:34 -07:00
GoaLitiuM e25448e10e Trade minor optimizations in MSVC Development builds for compile speed
Enabled the disabled optimization flags with whole program optimization
flag where the compilation speed doesn't seem to be affected at all, but
binary size is slightly smaller.
2024-03-31 19:05:35 +03:00
mafiesto4 d12630d815 Fix 2024-03-31 00:03:45 +01:00
mafiesto4 fb0d70d555 Fix 2024-03-30 23:58:50 +01:00
mafiesto4 c5e1abb08c Add setter for crword agent position and velocity 2024-03-30 22:29:43 +01:00
mafiesto4 294b4c4006 Add CPU profiler events for UI drawing 2024-03-30 22:20:41 +01:00
mafiesto4 e1944bce96 Add statically disabled geometry shaders on mobile 2024-03-30 22:08:44 +01:00
mafiesto4 369c19bd5d Add statically disabled tessellation on macOS/iOS 2024-03-30 18:46:37 +01:00
Zode 55383c3fa4 Patch numpad enter to normal enter on Linux 2024-03-30 02:35:11 +02:00
rkrahn 926a81c84b Merge branch 'FlaxEngine:master' into master 2024-03-29 13:40:36 -07:00
rkrahn d331e6b848 Merge remote-tracking branch 'upstream/master' 2024-03-29 13:38:38 -07:00
rkrahn eac553a992 Add toggle orthographic view hotkey
Adds the Toggle Orthographic hotkey to the editor. Bound to 'NumpadDecimal' by default.
2024-03-10 13:49:14 -07:00
261 changed files with 13036 additions and 2035 deletions
+4 -4
View File
@@ -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
+2 -2
View File
@@ -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.
+15 -18
View File
@@ -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
View File
@@ -3,8 +3,8 @@
"Version": {
"Major": 1,
"Minor": 8,
"Revision": 0,
"Build": 6510
"Revision": 1,
"Build": 6511
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
+5
View File
@@ -79,6 +79,9 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SDK/@EntryIndexedValue">SDK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=VS/@EntryIndexedValue">VS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=53eecf85_002Dd821_002D40e8_002Dac97_002Dfdb734542b84/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=70345118_002D4b40_002D4ece_002D937c_002Dbbeb7a0b2e70/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
@@ -220,6 +223,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002EFunctionReturnStyleSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECpp_002ECodeStyle_002ESettingsUpgrade_002ENamespaceIndentationSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
@@ -232,6 +236,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/GrammarAndSpelling/GrammarChecking/RulesStates/=LanguageTool_002EEN_002EE_005FG/@EntryIndexedValue">DisabledByUser</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/Color/@EntryValue">Blue</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/MatchComments/@EntryValue">True</s:Boolean>
@@ -133,6 +133,7 @@ namespace FlaxEditor.Content.Import
FileTypes["dds"] = ImportTexture;
FileTypes["hdr"] = ImportTexture;
FileTypes["raw"] = ImportTexture;
FileTypes["exr"] = ImportTexture;
// Models
FileTypes["obj"] = ImportModel;
@@ -128,6 +128,11 @@ namespace FlaxEditor.Content.Import
_settings.Settings.Type = TextureFormatType.HdrRGBA;
_settings.Settings.Compress = false;
}
else if (extension == ".exr")
{
// HDR image
_settings.Settings.Type = TextureFormatType.HdrRGBA;
}
else if (extension == ".hdr")
{
// HDR sky texture
@@ -249,6 +249,7 @@ namespace FlaxEditor.Content
private ScriptMemberInfo[] _parameters;
private ScriptMemberInfo[] _methods;
private object[] _attributes;
private List<Action<ScriptType>> _disposing;
/// <summary>
/// Gets the Visual Script asset that contains this type.
@@ -310,6 +311,13 @@ namespace FlaxEditor.Content
internal void Dispose()
{
if (_disposing != null)
{
foreach (var e in _disposing)
e(new ScriptType(this));
_disposing.Clear();
_disposing = null;
}
if (_parameters != null)
{
OnAssetReloading(_asset);
@@ -510,6 +518,14 @@ namespace FlaxEditor.Content
}
return _methods;
}
/// <inheritdoc />
public void TrackLifetime(Action<ScriptType> disposing)
{
if (_disposing == null)
_disposing = new List<Action<ScriptType>>();
_disposing.Add(disposing);
}
}
/// <summary>
@@ -135,7 +135,7 @@ namespace FlaxEditor.Content
}
Task.Run(() =>
{
Editor.CookMeshCollision(assetItem.Path, CollisionDataType.TriangleMesh, model);
Editor.CookMeshCollision(assetItem.Path, CollisionDataType.ConvexMesh, model);
if (created != null)
FlaxEngine.Scripting.InvokeOnUpdate(() => created(collisionData));
});
+1 -1
View File
@@ -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");
+1 -1
View File
@@ -536,7 +536,7 @@ namespace FlaxEditor.CustomEditors
/// </summary>
public void ClearReferenceValueAll()
{
Values.ClearReferenceValue();
Values?.ClearReferenceValue();
for (int i = 0; i < ChildrenEditors.Count; i++)
{
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Actions;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.CustomEditors.Elements;
@@ -9,6 +10,8 @@ using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Tree;
using FlaxEditor.Scripting;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
@@ -111,6 +114,38 @@ namespace FlaxEditor.CustomEditors.Dedicated
var actor = (Actor)Values[0];
var scriptType = TypeUtils.GetType(actor.TypeName);
var item = scriptType.ContentItem;
if (Presenter.Owner is PropertiesWindow propertiesWindow)
{
var lockButton = cm.AddButton(propertiesWindow.LockObjects ? "Unlock" : "Lock");
lockButton.ButtonClicked += button =>
{
propertiesWindow.LockObjects = !propertiesWindow.LockObjects;
// Reselect current selection
if (!propertiesWindow.LockObjects && Editor.Instance.SceneEditing.SelectionCount > 0)
{
var cachedSelection = Editor.Instance.SceneEditing.Selection.ToArray();
Editor.Instance.SceneEditing.Select(null);
Editor.Instance.SceneEditing.Select(cachedSelection);
}
};
}
else if (Presenter.Owner is PrefabWindow prefabWindow)
{
var lockButton = cm.AddButton(prefabWindow.LockSelectedObjects ? "Unlock" : "Lock");
lockButton.ButtonClicked += button =>
{
prefabWindow.LockSelectedObjects = !prefabWindow.LockSelectedObjects;
// Reselect current selection
if (!prefabWindow.LockSelectedObjects && prefabWindow.Selection.Count > 0)
{
var cachedSelection = prefabWindow.Selection.ToList();
prefabWindow.Select(null);
prefabWindow.Select(cachedSelection);
}
};
}
cm.AddButton("Copy ID", OnClickCopyId);
cm.AddButton("Edit actor type", OnClickEditActorType).Enabled = item != null;
var showButton = cm.AddButton("Show in content window", OnClickShowActorType);
@@ -164,7 +199,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
//Presenter.BuildLayoutOnUpdate();
// Better way is to just update the reference value using the new default instance of the prefab, created after changes apply
if (prefab && !prefab.WaitForLoaded())
if (Values != null && prefab && !prefab.WaitForLoaded())
{
var actor = (Actor)Values[0];
var prefabObjectId = actor.PrefabObjectID;
@@ -256,7 +291,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (editor.ChildrenEditors.Count == 0 || (isRefEdited && editor is CollectionEditor))
result = CreateDiffNode(editor);
bool isScriptEditorWithRefValue = editor is ScriptsEditor && editor.Values.HasReferenceValue;
bool isActorEditorInLevel = editor is ActorEditor && editor.Values[0] is Actor actor && actor.IsPrefabRoot && actor.Scene != null;
bool isActorEditorInLevel = editor is ActorEditor && editor.Values[0] is Actor actor && actor.IsPrefabRoot && actor.HasScene;
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
{
var childEditor = editor.ChildrenEditors[i];
@@ -45,6 +45,12 @@ public class ModelPrefabEditor : GenericEditor
break;
_prefabId = prefabObject.PrefabID;
}
else
{
// The model was removed earlier
_prefabId = Guid.Empty;
break;
}
}
var button = layout.Button("Reimport", "Reimports the source asset as prefab.");
@@ -37,6 +37,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
return;
}
EvaluateVisibleIf(itemLayout, item, GetLabelIndex(itemLayout, item));
// Add labels with a check box
var label = new CheckablePropertyNameLabel(item.DisplayName);
label.CheckBox.Tag = setting.Bit;
@@ -209,16 +209,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
{
var result = base.OnDragMove(ref location, data);
if (result != DragDropEffect.None)
if (result != DragDropEffect.None || _dragHandlers == null)
return result;
return _dragHandlers.Effect;
}
/// <inheritdoc />
public override void OnDragLeave()
{
_dragHandlers.OnDragLeave();
_dragHandlers?.OnDragLeave();
base.OnDragLeave();
}
@@ -31,6 +31,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// </summary>
private class EditTangentOptionBase
{
/// <summary>
/// Spline editor reference.
/// </summary>
public SplineEditor Editor;
/// <summary>
/// Called when user set selected tangent mode.
/// </summary>
@@ -103,7 +108,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
SetKeyframeLinear(spline, index);
// change the selection to tangent parent (a spline point / keyframe)
SetSelectSplinePointNode(spline, index);
Editor.SetSelectSplinePointNode(spline, index);
}
}
@@ -172,7 +177,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
public override void OnSetMode(Spline spline, int index)
{
SetTangentSmoothIn(spline, index);
SetSelectTangentIn(spline, index);
Editor.SetSelectTangentIn(spline, index);
}
}
@@ -186,7 +191,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
public override void OnSetMode(Spline spline, int index)
{
SetTangentSmoothOut(spline, index);
SetSelectTangentOut(spline, index);
Editor.SetSelectTangentOut(spline, index);
}
}
@@ -281,7 +286,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
base.Initialize(layout);
_currentTangentMode = new FreeTangentMode();
_currentTangentMode = new FreeTangentMode { Editor = this };
if (Values.HasDifferentTypes || !(Values[0] is Spline spline))
return;
_selectedSpline = spline;
@@ -471,7 +476,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (_currentTangentMode is LinearTangentMode)
return;
_currentTangentMode = new LinearTangentMode();
_currentTangentMode = new LinearTangentMode { Editor = this };
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
}
@@ -479,7 +484,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (_currentTangentMode is FreeTangentMode)
return;
_currentTangentMode = new FreeTangentMode();
_currentTangentMode = new FreeTangentMode { Editor = this };
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
}
@@ -487,7 +492,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (_currentTangentMode is AlignedTangentMode)
return;
_currentTangentMode = new AlignedTangentMode();
_currentTangentMode = new AlignedTangentMode { Editor = this };
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
}
@@ -495,7 +500,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (_currentTangentMode is SmoothInTangentMode)
return;
_currentTangentMode = new SmoothInTangentMode();
_currentTangentMode = new SmoothInTangentMode { Editor = this };
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
}
@@ -503,17 +508,34 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (_currentTangentMode is SmoothOutTangentMode)
return;
_currentTangentMode = new SmoothOutTangentMode();
_currentTangentMode = new SmoothOutTangentMode { Editor = this };
_currentTangentMode.OnSetMode(_selectedSpline, _lastPointSelected.Index);
}
private List<SceneGraphNode> GetSelection()
{
if (Presenter.Owner is Windows.Assets.PrefabWindow prefabWindow)
return prefabWindow.Selection;
return Editor.Instance.SceneEditing.Selection;
}
private void Select(SceneGraphNode node)
{
if (Presenter.Owner is Windows.Assets.PrefabWindow prefabWindow)
{
prefabWindow.Select(node);
return;
}
Editor.Instance.SceneEditing.Select(node);
}
private void UpdateSelectedPoint()
{
// works only if select one spline
if (_selectedSpline)
{
var currentSelected = Editor.Instance.SceneEditing.Selection[0];
var selection = GetSelection();
var currentSelected = selection.Count != 0 ? selection[0] : null;
if (currentSelected == _selectedPoint)
return;
if (currentSelected is SplineNode.SplinePointNode selectedPoint)
@@ -540,15 +562,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
private void UpdateSelectedTangent()
{
// works only if select one spline
if (_lastPointSelected == null || Editor.Instance.SceneEditing.SelectionCount != 1)
var selection = GetSelection();
if (_lastPointSelected == null || selection.Count != 1)
{
_selectedTangentIn = null;
_selectedTangentOut = null;
return;
}
var currentSelected = Editor.Instance.SceneEditing.Selection[0];
var currentSelected = selection[0];
if (currentSelected is not SplineNode.SplinePointTangentNode selectedPoint)
{
_selectedTangentIn = null;
@@ -568,10 +589,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
_selectedTangentIn = selectedPoint;
_selectedTangentOut = null;
_currentTangentMode.OnSelectTangent(_selectedSpline, index);
return;
}
if (currentSelected.Transform == _selectedSpline.GetSplineTangent(index, false))
{
_selectedTangentOut = selectedPoint;
@@ -833,7 +852,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
return null;
}
private static SplineNode.SplinePointTangentNode GetSplineTangentOutNode(Spline spline, int index)
private SplineNode.SplinePointTangentNode GetSplineTangentOutNode(Spline spline, int index)
{
var point = GetSplinePointNode(spline, index);
var tangentOut = spline.GetSplineTangent(index, false);
@@ -851,19 +870,19 @@ namespace FlaxEditor.CustomEditors.Dedicated
return null;
}
private static void SetSelectSplinePointNode(Spline spline, int index)
private void SetSelectSplinePointNode(Spline spline, int index)
{
Editor.Instance.SceneEditing.Select(GetSplinePointNode(spline, index));
Select(GetSplinePointNode(spline, index));
}
private static void SetSelectTangentIn(Spline spline, int index)
private void SetSelectTangentIn(Spline spline, int index)
{
Editor.Instance.SceneEditing.Select(GetSplineTangentInNode(spline, index));
Select(GetSplineTangentInNode(spline, index));
}
private static void SetSelectTangentOut(Spline spline, int index)
private void SetSelectTangentOut(Spline spline, int index)
{
Editor.Instance.SceneEditing.Select(GetSplineTangentOutNode(spline, index));
Select(GetSplineTangentOutNode(spline, index));
}
}
}
@@ -585,7 +585,7 @@ namespace FlaxEditor.CustomEditors.Editors
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
{
var result = base.OnDragMove(ref location, data);
if (result != DragDropEffect.None)
if (result != DragDropEffect.None || _dragHandlers == null)
return result;
return _dragHandlers.Effect;
@@ -594,7 +594,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// <inheritdoc />
public override void OnDragLeave()
{
_dragHandlers.OnDragLeave();
_dragHandlers?.OnDragLeave();
base.OnDragLeave();
}
@@ -582,55 +582,13 @@ namespace FlaxEditor.CustomEditors.Editors
}
/// <summary>
/// Spawns the property for the given item.
/// Evaluate the <see cref="VisibleIfAttribute"/> cache for a given property item.
/// </summary>
/// <param name="itemLayout">The item layout.</param>
/// <param name="itemValues">The item values.</param>
/// <param name="item">The item.</param>
protected virtual void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
/// <param name="labelIndex">The label index.</param>
protected virtual void EvaluateVisibleIf(LayoutElementsContainer itemLayout, ItemInfo item, int labelIndex)
{
int labelIndex = 0;
if ((item.IsReadOnly || item.VisibleIfs.Length > 0) &&
itemLayout.Children.Count > 0 &&
itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement propertiesListElement)
{
labelIndex = propertiesListElement.Labels.Count;
}
itemLayout.Property(item.DisplayName, itemValues, item.OverrideEditor, item.TooltipText);
if (item.IsReadOnly && itemLayout.Children.Count > 0)
{
PropertiesListElement list = null;
int firstChildControlIndex = 0;
bool disableSingle = true;
var control = itemLayout.Children[itemLayout.Children.Count - 1];
if (control is GroupElement group && group.Children.Count > 0)
{
list = group.Children[0] as PropertiesListElement;
disableSingle = false; // Disable all nested editors
}
else if (control is PropertiesListElement list1)
{
list = list1;
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
}
if (list != null)
{
// Disable controls added to the editor
var count = list.Properties.Children.Count;
for (int j = firstChildControlIndex; j < count; j++)
{
var child = list.Properties.Children[j];
if (disableSingle && child is PropertyNameLabel)
break;
if (child != null)
child.Enabled = false;
}
}
}
if (item.VisibleIfs.Length > 0 && itemLayout.Children.Count > 0)
{
PropertiesListElement list = null;
@@ -669,6 +627,73 @@ namespace FlaxEditor.CustomEditors.Editors
}
}
/// <summary>
/// Get the label index.
/// </summary>
/// <param name="itemLayout">The item layout.</param>
/// <param name="item">The item.</param>
/// <returns>The label index.</returns>
protected virtual int GetLabelIndex(LayoutElementsContainer itemLayout, ItemInfo item)
{
int labelIndex = 0;
if ((item.IsReadOnly || item.VisibleIfs.Length > 0) &&
itemLayout.Children.Count > 0 &&
itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement propertiesListElement)
{
labelIndex = propertiesListElement.Labels.Count;
}
return labelIndex;
}
/// <summary>
/// Spawns the property for the given item.
/// </summary>
/// <param name="itemLayout">The item layout.</param>
/// <param name="itemValues">The item values.</param>
/// <param name="item">The item.</param>
protected virtual void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
{
int labelIndex = GetLabelIndex(itemLayout, item);
itemLayout.Property(item.DisplayName, itemValues, item.OverrideEditor, item.TooltipText);
if (item.IsReadOnly && itemLayout.Children.Count > 0)
{
PropertiesListElement list = null;
int firstChildControlIndex = 0;
bool disableSingle = true;
var control = itemLayout.Children[itemLayout.Children.Count - 1];
if (control is GroupElement group && group.Children.Count > 0)
{
list = group.Children[0] as PropertiesListElement;
disableSingle = false; // Disable all nested editors
}
else if (control is PropertiesListElement list1)
{
list = list1;
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
}
if (list != null)
{
// Disable controls added to the editor
var count = list.Properties.Children.Count;
for (int j = firstChildControlIndex; j < count; j++)
{
var child = list.Properties.Children[j];
if (disableSingle && child is PropertyNameLabel)
break;
if (child != null)
child.Enabled = false;
}
}
}
EvaluateVisibleIf(itemLayout, item, labelIndex);
}
/// <inheritdoc />
internal override void Initialize(CustomEditorPresenter presenter, LayoutElementsContainer layout, ValueContainer values)
{
@@ -38,7 +38,7 @@ namespace FlaxEditor.CustomEditors.Editors
_comboBox.SelectedIndexChanged += OnSelectedIndexChanged;
}
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor)
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor)
{
var button = menu.AddButton("Set to null");
button.Clicked += () => _comboBox.SelectedItem = null;
@@ -106,7 +106,7 @@ namespace FlaxEditor.CustomEditors.Editors
_comboBox.SelectedIndexChanged += OnSelectedIndexChanged;
}
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkededitor)
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor)
{
var button = menu.AddButton("Set to null");
button.Clicked += () => _comboBox.SelectedItem = null;
@@ -79,7 +79,7 @@ namespace FlaxEditor.CustomEditors
var theFirstType = TypeUtils.GetObjectType(this[0]);
for (int i = 1; i < Count; i++)
{
if (theFirstType != TypeUtils.GetObjectType(this[1]))
if (theFirstType != TypeUtils.GetObjectType(this[i]))
return true;
}
return false;
+1 -1
View File
@@ -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;
+6 -5
View File
@@ -42,15 +42,14 @@ namespace FlaxEditor.GUI.ContextMenu
// Arrange controls
Margin margin = _menu._itemsMargin;
float y = margin.Top;
float x = margin.Left;
float y = 0;
float width = Width - margin.Width;
for (int i = 0; i < _children.Count; i++)
{
if (_children[i] is ContextMenuItem item && item.Visible)
{
var height = item.Height;
item.Bounds = new Rectangle(x, y, width, height);
item.Bounds = new Rectangle(margin.Left, y, width, height);
y += height + margin.Height;
}
}
@@ -300,7 +299,6 @@ namespace FlaxEditor.GUI.ContextMenu
if (_panel.Children[i] is ContextMenuChildMenu menu && menu.Text == text)
return menu;
}
return null;
}
@@ -319,7 +317,6 @@ namespace FlaxEditor.GUI.ContextMenu
Parent = _panel
};
}
return item;
}
@@ -396,10 +393,12 @@ namespace FlaxEditor.GUI.ContextMenu
float height = _itemsAreaMargin.Height;
int itemsLeft = MaximumItemsInViewCount;
int overflowItemCount = 0;
int itemsCount = 0;
for (int i = 0; i < _panel.Children.Count; i++)
{
if (_panel.Children[i] is ContextMenuItem item && item.Visible)
{
itemsCount++;
if (itemsLeft > 0)
{
height += item.Height + _itemsMargin.Height;
@@ -412,6 +411,8 @@ namespace FlaxEditor.GUI.ContextMenu
maxWidth = Mathf.Max(maxWidth, item.MinimumWidth);
}
}
if (itemsCount != 0)
height -= _itemsMargin.Height; // Remove item margin from top and bottom
maxWidth = Mathf.Max(maxWidth + 20, MinimumWidth);
// Move child arrows to accommodate scroll bar showing
@@ -29,6 +29,15 @@ namespace FlaxEditor.GUI.ContextMenu
CloseMenuOnClick = false;
}
private void ShowChild(ContextMenu parentContextMenu)
{
// Hide parent CM popups and set itself as child
var vAlign = parentContextMenu.ItemsAreaMargin.Top;
var location = new Float2(Width, -vAlign);
location = PointToParent(parentContextMenu, location);
parentContextMenu.ShowChild(ContextMenu, location);
}
/// <inheritdoc />
public override void Draw()
{
@@ -58,14 +67,12 @@ namespace FlaxEditor.GUI.ContextMenu
var parentContextMenu = ParentContextMenu;
if (parentContextMenu == ContextMenu)
return;
if (ContextMenu.IsOpened)
return;
base.OnMouseEnter(location);
// Hide parent CM popups and set itself as child
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
ShowChild(parentContextMenu);
}
/// <inheritdoc />
@@ -78,8 +85,7 @@ namespace FlaxEditor.GUI.ContextMenu
if (ContextMenu.IsOpened)
return true;
// Hide parent CM popups and set itself as child
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
ShowChild(parentContextMenu);
return base.OnMouseUp(location, button);
}
}
@@ -47,7 +47,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
break;
default: throw new ArgumentOutOfRangeException();
}
var color = (_timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground).AlphaMultiplied(0.6f);
var color = (_timeline.IsMovingPositionHandle ? style.SelectionBorder : style.Foreground).AlphaMultiplied(0.6f);
Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1);
var m2 = Matrix3x3.Translation2D(0, timeAxisHeaderOffset);
Matrix3x3.Multiply(ref m1, ref m2, out var m3);
@@ -61,7 +61,8 @@ namespace FlaxEditor.GUI.Timeline.GUI
Render2D.DrawText(style.FontSmall, labelText, style.Foreground, new Float2(2, -6));
Render2D.PopTransform();
Render2D.FillRectangle(new Rectangle(Width * 0.5f, Height + timeAxisHeaderOffset, 1, _timeline.MediaPanel.Height - timeAxisHeaderOffset - timeAxisOverlap), _timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground.RGBMultiplied(0.8f));
color = _timeline.IsMovingPositionHandle ? style.SelectionBorder : style.Foreground.RGBMultiplied(0.8f);
Render2D.FillRectangle(new Rectangle(Width * 0.5f, Height + timeAxisHeaderOffset, 1, _timeline.MediaPanel.Height - timeAxisHeaderOffset - timeAxisOverlap), color);
base.Draw();
}
@@ -42,7 +42,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
var timeAxisOverlap = Timeline.HeaderTopAreaHeight * 0.5f;
var timeAxisHeaderOffset = -_timeline.MediaBackground.ViewOffset.Y - timeAxisOverlap;
var moveColor = style.ProgressNormal;
var moveColor = style.SelectionBorder;
var thickness = 2.0f;
var borderColor = _isMoving ? moveColor : (IsMouseOver && _canEdit ? Color.Yellow : style.BorderNormal);
Render2D.FillRectangle(new Rectangle((Width - thickness) * 0.5f, timeAxisHeaderOffset, thickness, Height - timeAxisHeaderOffset), borderColor);
+2 -2
View File
@@ -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()
{
+73 -8
View File
@@ -38,7 +38,6 @@ namespace FlaxEditor.GUI.Tree
private bool _isMouseDown;
private float _mouseDownTime;
private Float2 _mouseDownPos;
private Color _cachedTextColor;
private DragItemPositioning _dragOverMode;
private bool _isDragOverHeader;
@@ -91,6 +90,11 @@ namespace FlaxEditor.GUI.Tree
}
}
/// <summary>
/// Gets a value indicating whether the node is collapsed in the hierarchy (is collapsed or any of its parents is collapsed).
/// </summary>
public bool IsCollapsedInHierarchy => IsCollapsed || (Parent is TreeNode parentNode && parentNode.IsCollapsedInHierarchy);
/// <summary>
/// Gets or sets the text margin.
/// </summary>
@@ -604,9 +608,6 @@ namespace FlaxEditor.GUI.Tree
/// <inheritdoc />
public override void Update(float deltaTime)
{
// Cache text color
_cachedTextColor = CacheTextColor();
// Drop/down animation
if (_animationProgress < 1.0f)
{
@@ -676,7 +677,8 @@ namespace FlaxEditor.GUI.Tree
}
// Draw text
Render2D.DrawText(TextFont.GetFont(), _text, textRect, _cachedTextColor, TextAlignment.Near, TextAlignment.Center);
Color textColor = CacheTextColor();
Render2D.DrawText(TextFont.GetFont(), _text, textRect, textColor, TextAlignment.Near, TextAlignment.Center);
// Draw drag and drop effect
if (IsDragOver && _tree.DraggedOverNode == this)
@@ -712,6 +714,71 @@ namespace FlaxEditor.GUI.Tree
}
}
/// <inheritdoc />
protected override void DrawChildren()
{
// Draw all visible child controls
var children = _children;
if (children.Count == 0)
return;
if (CullChildren)
{
Render2D.PeekClip(out var globalClipping);
Render2D.PeekTransform(out var globalTransform);
// Try to estimate the rough location of the first node, assuming the node height is constant
var firstChildGlobalRect = GetChildGlobalRectangle(children[0], ref globalTransform);
var firstVisibleChild = Math.Clamp((int)Math.Floor((globalClipping.Y - firstChildGlobalRect.Top) / firstChildGlobalRect.Height) + 1, 0, children.Count - 1);
if (GetChildGlobalRectangle(children[firstVisibleChild], ref globalTransform).Top > globalClipping.Top)
{
// Overshoot...
for (; firstVisibleChild > 0; firstVisibleChild--)
{
var child = children[firstVisibleChild];
if (GetChildGlobalRectangle(child, ref globalTransform).Top < globalClipping.Top)
break;
}
}
for (int i = firstVisibleChild; i < children.Count; i++)
{
var child = children[i];
if (child.Visible)
{
var childGlobalRect = GetChildGlobalRectangle(child, ref globalTransform);
if (globalClipping.Intersects(ref childGlobalRect))
{
Render2D.PushTransform(ref child._cachedTransform);
child.Draw();
Render2D.PopTransform();
}
else
break;
}
}
static Rectangle GetChildGlobalRectangle(Control control, ref Matrix3x3 globalTransform)
{
Matrix3x3.Multiply(ref control._cachedTransform, ref globalTransform, out var globalChildTransform);
return new Rectangle(globalChildTransform.M31, globalChildTransform.M32, control.Width * globalChildTransform.M11, control.Height * globalChildTransform.M22);
}
}
else
{
for (int i = 0; i < children.Count; i++)
{
var child = children[i];
if (child.Visible)
{
Render2D.PushTransform(ref child._cachedTransform);
child.Draw();
Render2D.PopTransform();
}
}
}
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
@@ -996,7 +1063,7 @@ namespace FlaxEditor.GUI.Tree
// Expand node if mouse goes over arrow
if (ArrowRect.Contains(location) && HasAnyVisibleChild)
Expand(true);
if (!_isDragOverHeader)
result = OnDragEnterHeader(data);
else
@@ -1104,7 +1171,6 @@ namespace FlaxEditor.GUI.Tree
{
// TODO: perform layout for any non-TreeNode controls
_cachedHeight = _headerHeight;
_cachedTextColor = CacheTextColor();
Size = new Float2(width, _headerHeight);
}
@@ -1154,7 +1220,6 @@ namespace FlaxEditor.GUI.Tree
}
_cachedHeight = height;
_cachedTextColor = CacheTextColor();
Height = Mathf.Max(_headerHeight, y);
}
+24
View File
@@ -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());
+1
View File
@@ -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);
+1 -1
View File
@@ -227,7 +227,7 @@ namespace FlaxEditor.Modules
// When applying changes to prefab from actor in level ignore it's root transformation (see ActorEditor.ProcessDiff)
var originalTransform = instance.LocalTransform;
if (instance.IsPrefabRoot && instance.Scene != null)
if (instance.IsPrefabRoot && instance.HasScene)
instance.LocalTransform = prefab.GetDefaultInstance().Transform;
// Call backend
+14 -15
View File
@@ -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();
}
+4
View File
@@ -320,6 +320,10 @@ namespace FlaxEditor.Options
[EditorDisplay("Viewport"), EditorOrder(1750)]
public InputBinding ViewpointBottom = new InputBinding(KeyboardKeys.Numpad2);
[DefaultValue(typeof(InputBinding), "NumpadDecimal")]
[EditorDisplay("Viewport"), EditorOrder(1760)]
public InputBinding ToggleOrthographic = new InputBinding(KeyboardKeys.NumpadDecimal);
#endregion
#region Interface
@@ -49,12 +49,14 @@ namespace FlaxEditor.SceneGraph.Actors
{
get
{
var actor = (Spline)_node.Actor;
return actor.GetSplineTransform(Index);
var actor = (Spline)_node?.Actor;
return actor?.GetSplineTransform(Index) ?? Transform.Identity;
}
set
{
var actor = (Spline)_node.Actor;
var actor = (Spline)_node?.Actor;
if (actor == null)
return;
actor.SetSplineTransform(Index, value);
OnSplineEdited(actor);
}
@@ -91,15 +91,11 @@ namespace FlaxEditor.SceneGraph.Actors
private void OnAddMeshCollider(EditorWindow window)
{
// Allow collider to be added to evey static model selection
SceneGraphNode[] selection = Array.Empty<SceneGraphNode>();
var selection = Array.Empty<SceneGraphNode>();
if (window is SceneTreeWindow)
{
selection = Editor.Instance.SceneEditing.Selection.ToArray();
}
else if (window is PrefabWindow prefabWindow)
{
selection = prefabWindow.Selection.ToArray();
}
var createdNodes = new List<SceneGraphNode>();
foreach (var node in selection)
@@ -68,7 +68,7 @@ namespace FlaxEditor.SceneGraph.GUI
Visible = (actor.HideFlags & HideFlags.HideInHierarchy) == 0;
// Pick the correct id when inside a prefab window.
var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
var id = actor.HasPrefabLink && !actor.HasScene ? actor.PrefabObjectID : actor.ID;
if (Editor.Instance.ProjectCache.IsExpandedActor(ref id))
{
Expand(true);
@@ -97,7 +97,7 @@ namespace FlaxEditor.SceneGraph.GUI
parentTreeNode.IsLayoutLocked = false;
// Skip UI update if node won't be in a view
if (parentTreeNode.IsCollapsed)
if (parentTreeNode.IsCollapsedInHierarchy)
{
UnlockChildrenRecursive();
}
@@ -291,7 +291,7 @@ namespace FlaxEditor.SceneGraph.GUI
return Style.Current.ForegroundGrey;
}
if (actor.Scene != null && Editor.Instance.StateMachine.IsPlayMode && actor.IsStatic)
if (actor.HasScene && Editor.Instance.StateMachine.IsPlayMode && actor.IsStatic)
{
// Static
return color * 0.85f;
@@ -366,7 +366,7 @@ namespace FlaxEditor.SceneGraph.GUI
if (!IsLayoutLocked && actor)
{
// Pick the correct id when inside a prefab window.
var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
var id = actor.HasPrefabLink && !actor.HasScene ? actor.PrefabObjectID : actor.ID;
Editor.Instance.ProjectCache.SetExpandedActor(ref id, IsExpanded);
}
}
@@ -640,8 +640,8 @@ namespace FlaxEditor.SceneGraph.GUI
private bool ValidateDragScript(Script script)
{
// Reject dragging scripts not linked to scene (eg. from prefab) or in the opposite way
var thisHasScene = Actor.Scene != null;
var otherHasScene = script.Scene != null;
var thisHasScene = Actor.HasScene;
var otherHasScene = script.HasScene;
if (thisHasScene != otherHasScene)
return false;
+1 -1
View File
@@ -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>
+14 -4
View File
@@ -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);
+5 -1
View File
@@ -163,6 +163,8 @@ namespace FlaxEditor.States
Editor.OnPlayBegin();
IsPlayModeStarting = false;
Profiler.EndEvent();
Time.Synchronize();
}
private void SetupEditorEnvOptions()
@@ -192,7 +194,7 @@ namespace FlaxEditor.States
// Restore editor scene
SceneRestoring?.Invoke();
_duplicateScenes.DeletedScenes();
_duplicateScenes.UnloadScenes();
PluginManager.Internal_DeinitializeGamePlugins();
Editor.Internal_SetPlayMode(false);
_duplicateScenes.RestoreSceneData();
@@ -209,6 +211,8 @@ namespace FlaxEditor.States
Editor.OnPlayEnd();
IsPlayModeEnding = false;
Profiler.EndEvent();
Time.Synchronize();
}
}
}
@@ -6,6 +6,7 @@ using System.IO;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Input;
using FlaxEditor.Scripting;
using FlaxEditor.Surface.Undo;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -16,14 +17,13 @@ namespace FlaxEditor.Surface.Archetypes
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
[HideInEditor]
public abstract class BlendPointsEditor : ContainerControl
public class BlendPointsEditor : ContainerControl
{
private readonly Animation.MultiBlend _node;
private readonly bool _is2D;
private Float2 _rangeX;
private Float2 _rangeY;
private readonly BlendPoint[] _blendPoints = new BlendPoint[Animation.MultiBlend.MaxAnimationsCount];
private readonly Guid[] _pointsAnims = new Guid[Animation.MultiBlend.MaxAnimationsCount];
private readonly Float2[] _pointsLocations = new Float2[Animation.MultiBlend.MaxAnimationsCount];
private Float2 _rangeX, _rangeY;
private Float2 _debugPos = Float2.Minimum;
private readonly List<BlendPoint> _blendPoints = new List<BlendPoint>();
/// <summary>
/// Represents single blend point.
@@ -31,16 +31,22 @@ namespace FlaxEditor.Surface.Archetypes
/// <seealso cref="FlaxEngine.GUI.Control" />
protected class BlendPoint : Control
{
private static Matrix3x3 _transform = Matrix3x3.RotationZ(45.0f * Mathf.DegreesToRadians) * Matrix3x3.Translation2D(4.0f, 0.5f);
private readonly BlendPointsEditor _editor;
private readonly int _index;
private bool _isMouseDown;
private Float2 _mousePosOffset;
private bool _isMouseDown, _mouseMoved;
private object[] _mouseMoveStartValues;
/// <summary>
/// The default size for the blend points.
/// </summary>
public const float DefaultSize = 8.0f;
/// <summary>
/// Blend point index.
/// </summary>
public int Index => _index;
/// <summary>
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
/// </summary>
@@ -53,24 +59,45 @@ namespace FlaxEditor.Surface.Archetypes
_index = index;
}
private void EndMove()
{
_isMouseDown = false;
EndMouseCapture();
if (_mouseMoveStartValues != null)
{
// Add undo action
_editor._node.Surface.AddBatchedUndoAction(new EditNodeValuesAction(_editor._node, _mouseMoveStartValues, true));
_mouseMoveStartValues = null;
}
if (_mouseMoved)
_editor._node.Surface.MarkAsEdited();
}
/// <inheritdoc />
public override void OnGotFocus()
{
base.OnGotFocus();
_editor.SelectedIndex = _index;
_editor._node.SelectedAnimationIndex = _index;
}
/// <inheritdoc />
public override void Draw()
{
// Cache data
var isSelected = _editor.SelectedIndex == _index;
// Draw rotated rectangle
Render2D.PushTransform(ref _transform);
Render2D.FillRectangle(new Rectangle(0, 0, 5, 5), isSelected ? Color.Orange : Color.BlueViolet);
Render2D.PopTransform();
// Draw dot with outline
var style = Style.Current;
var icon = Editor.Instance.Icons.VisjectBoxClosed32;
var size = Height;
var rect = new Rectangle(new Float2(size * -0.5f) + Size * 0.5f, new Float2(size));
var outline = Color.Black; // Shadow
if (_isMouseDown)
outline = style.SelectionBorder;
else if (IsMouseOver)
outline = style.BorderHighlighted;
else if (_editor._node.SelectedAnimationIndex == _index)
outline = style.BackgroundSelected;
Render2D.DrawSprite(icon, rect.MakeExpanded(4.0f), outline);
Render2D.DrawSprite(icon, rect, style.Foreground);
}
/// <inheritdoc />
@@ -80,6 +107,9 @@ namespace FlaxEditor.Surface.Archetypes
{
Focus();
_isMouseDown = true;
_mouseMoved = false;
_mouseMoveStartValues = null;
_mousePosOffset = -location;
StartMouseCapture();
return true;
}
@@ -92,8 +122,7 @@ namespace FlaxEditor.Surface.Archetypes
{
if (button == MouseButton.Left && _isMouseDown)
{
_isMouseDown = false;
EndMouseCapture();
EndMove();
return true;
}
@@ -103,9 +132,26 @@ namespace FlaxEditor.Surface.Archetypes
/// <inheritdoc />
public override void OnMouseMove(Float2 location)
{
if (_isMouseDown)
if (_isMouseDown && (_mouseMoved || Float2.DistanceSquared(location, _mousePosOffset) > 16.0f))
{
_editor.SetLocation(_index, _editor.BlendPointPosToBlendSpacePos(Location + location));
if (!_mouseMoved)
{
// Capture initial state for undo
_mouseMoved = true;
_mouseMoveStartValues = _editor._node.Surface.Undo != null ? (object[])_editor._node.Values.Clone() : null;
}
var newLocation = Location + location + _mousePosOffset;
newLocation = _editor.BlendPointPosToBlendSpacePos(newLocation);
if (Root != null && Root.GetKey(KeyboardKeys.Control))
{
var data0 = (Float4)_editor._node.Values[0];
var rangeX = new Float2(data0.X, data0.Y);
var rangeY = _editor._is2D ? new Float2(data0.Z, data0.W) : Float2.One;
var grid = new Float2(Mathf.Abs(rangeX.Y - rangeX.X) * 0.01f, Mathf.Abs(rangeY.X - rangeY.Y) * 0.01f);
newLocation = Float2.SnapToGrid(newLocation, grid);
}
_editor.SetLocation(_index, newLocation);
}
base.OnMouseMove(location);
@@ -116,8 +162,7 @@ namespace FlaxEditor.Surface.Archetypes
{
if (_isMouseDown)
{
_isMouseDown = false;
EndMouseCapture();
EndMove();
}
base.OnMouseLeave();
@@ -128,8 +173,7 @@ namespace FlaxEditor.Surface.Archetypes
{
if (_isMouseDown)
{
_isMouseDown = false;
EndMouseCapture();
EndMove();
}
base.OnLostFocus();
@@ -149,40 +193,130 @@ namespace FlaxEditor.Surface.Archetypes
/// </summary>
public bool Is2D => _is2D;
/// <summary>
/// Blend points count.
/// </summary>
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
/// <summary>
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
/// </summary>
/// <param name="node">The node.</param>
/// <param name="is2D">The value indicating whether blend space is 2D, otherwise it is 1D.</param>
/// <param name="x">The X location.</param>
/// <param name="y">The Y location.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public BlendPointsEditor(bool is2D, float x, float y, float width, float height)
public BlendPointsEditor(Animation.MultiBlend node, bool is2D, float x, float y, float width, float height)
: base(x, y, width, height)
{
_node = node;
_is2D = is2D;
}
/// <summary>
/// Gets the blend space data.
/// </summary>
/// <param name="rangeX">The space range for X axis (X-width, Y-height).</param>
/// <param name="rangeY">The space range for Y axis (X-width, Y-height).</param>
/// <param name="pointsAnims">The points anims (input array to fill of size equal 14).</param>
/// <param name="pointsLocations">The points locations (input array to fill of size equal 14).</param>
public abstract void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations);
internal void AddPoint()
{
// Add random point within range
var rand = new Float2(Mathf.Frac((float)Platform.TimeSeconds), (Platform.TimeCycles % 10000) / 10000.0f);
AddPoint(Float2.Lerp(new Float2(_rangeX.X, _rangeY.X), new Float2(_rangeX.Y, _rangeY.Y), rand));
}
private void AddPoint(Float2 location)
{
// Reuse existing animation
var count = PointsCount;
Guid id = Guid.Empty;
for (int i = 0; i < count; i++)
{
id = (Guid)_node.Values[5 + i * 2];
if (id != Guid.Empty)
break;
}
if (id == Guid.Empty)
{
// Just use the first anim from project, user will change it
var ids = FlaxEngine.Content.GetAllAssetsByType(typeof(FlaxEngine.Animation));
if (ids.Length != 0)
id = ids[0];
else
return;
}
AddPoint(id, location);
}
/// <summary>
/// Gets or sets the index of the selected blend point.
/// Sets the blend point asset.
/// </summary>
public abstract int SelectedIndex { get; set; }
/// <param name="asset">The asset.</param>
/// <param name="location">The location.</param>
public void AddPoint(Guid asset, Float2 location)
{
// Find the first free slot
var count = PointsCount;
if (count == Animation.MultiBlend.MaxAnimationsCount)
return;
var values = (object[])_node.Values.Clone();
var index = 0;
for (; index < count; index++)
{
var dataB = (Guid)_node.Values[5 + index * 2];
if (dataB == Guid.Empty)
break;
}
if (index == count)
{
// Add another blend point
Array.Resize(ref values, values.Length + 2);
}
values[4 + index * 2] = new Float4(location.X, _is2D ? location.Y : 0.0f, 0, 1.0f);
values[5 + index * 2] = asset;
_node.SetValues(values);
// Auto-select
_node.SelectedAnimationIndex = index;
}
/// <summary>
/// Sets the blend point asset.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="asset">The asset.</param>
/// <param name="withUndo">True to use undo action.</param>
public void SetAsset(int index, Guid asset, bool withUndo = true)
{
if (withUndo)
{
_node.SetValue(5 + index * 2, asset);
}
else
{
_node.Values[5 + index * 2] = asset;
_node.Surface.MarkAsEdited();
}
_node.UpdateUI();
}
/// <summary>
/// Sets the blend point location.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="location">The location.</param>
public abstract void SetLocation(int index, Float2 location);
public void SetLocation(int index, Float2 location)
{
var dataA = (Float4)_node.Values[4 + index * 2];
var ranges = (Float4)_node.Values[0];
dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y);
if (_is2D)
dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W);
_node.Values[4 + index * 2] = dataA;
_node.UpdateUI();
}
/// <summary>
/// Gets the blend points area.
@@ -249,10 +383,23 @@ namespace FlaxEditor.Surface.Archetypes
public override void Update(float deltaTime)
{
// Synchronize blend points collection
GetData(out _rangeX, out _rangeY, _pointsAnims, _pointsLocations);
for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++)
var data0 = (Float4)_node.Values[0];
_rangeX = new Float2(data0.X, data0.Y);
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
var count = PointsCount;
while (_blendPoints.Count > count)
{
if (_pointsAnims[i] != Guid.Empty)
_blendPoints[count].Dispose();
_blendPoints.RemoveAt(count);
}
while (_blendPoints.Count < count)
_blendPoints.Add(null);
for (int i = 0; i < count; i++)
{
var animId = (Guid)_node.Values[5 + i * 2];
var dataA = (Float4)_node.Values[4 + i * 2];
var location = new Float2(Mathf.Clamp(dataA.X, _rangeX.X, _rangeX.Y), _is2D ? Mathf.Clamp(dataA.Y, _rangeY.X, _rangeY.Y) : 0.0f);
if (animId != Guid.Empty)
{
if (_blendPoints[i] == null)
{
@@ -264,7 +411,13 @@ namespace FlaxEditor.Surface.Archetypes
}
// Update blend point
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(_pointsLocations[i]);
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location);
var asset = Editor.Instance.ContentDatabase.FindAsset(animId);
var tooltip = asset?.ShortName ?? string.Empty;
tooltip += "\nX: " + location.X;
if (_is2D)
tooltip += "\nY: " + location.Y;
_blendPoints[i].TooltipText = tooltip;
}
else
{
@@ -277,6 +430,26 @@ namespace FlaxEditor.Surface.Archetypes
}
}
// Debug current playback position
if (((AnimGraphSurface)_node.Surface).TryGetTraceEvent(_node, out var traceEvent))
{
if (_is2D)
{
unsafe
{
// Unpack xy from 32-bits
Half2 packed = *(Half2*)&traceEvent.Value;
_debugPos = (Float2)packed;
}
}
else
_debugPos = new Float2(traceEvent.Value, 0.0f);
}
else
{
_debugPos = Float2.Minimum;
}
base.Update(deltaTime);
}
@@ -293,14 +466,90 @@ namespace FlaxEditor.Surface.Archetypes
}
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (base.OnMouseDown(location, button))
return true;
Focus();
return true;
}
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (base.OnMouseUp(location, button))
return true;
if (button == MouseButton.Right)
{
// Show context menu
var menu = new FlaxEditor.GUI.ContextMenu.ContextMenu();
var b = menu.AddButton("Add point", OnAddPoint);
b.Tag = location;
b.Enabled = PointsCount < Animation.MultiBlend.MaxAnimationsCount;
if (GetChildAt(location) is BlendPoint blendPoint)
{
b = menu.AddButton("Remove point", OnRemovePoint);
b.Tag = blendPoint.Index;
b.TooltipText = blendPoint.TooltipText;
}
menu.Show(this, location);
}
return true;
}
private void OnAddPoint(FlaxEditor.GUI.ContextMenu.ContextMenuButton b)
{
AddPoint(BlendPointPosToBlendSpacePos((Float2)b.Tag));
}
private void OnRemovePoint(FlaxEditor.GUI.ContextMenu.ContextMenuButton b)
{
SetAsset((int)b.Tag, Guid.Empty);
}
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
{
// Draw line
Render2D.DrawLine(start, end, gridColor);
// Draw label
var labelWidth = 50.0f;
var labelHeight = 10.0f;
var labelMargin = 2.0f;
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
var hAlign = TextAlignment.Near;
Rectangle labelRect;
if (vertical)
{
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
if (isLast)
return; // Don't overlap with the first horizontal label
}
else
{
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
if (isLast)
{
labelRect.X = start.X - labelMargin - labelRect.Width;
hAlign = TextAlignment.Far;
}
}
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
}
/// <inheritdoc />
public override void Draw()
{
// Cache data
var style = Style.Current;
var rect = new Rectangle(Float2.Zero, Size);
var containsFocus = ContainsFocus;
GetPointsArea(out var pointsArea);
var data0 = (Float4)_node.Values[0];
var rangeX = new Float2(data0.X, data0.Y);
// Background
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
@@ -309,19 +558,27 @@ namespace FlaxEditor.Surface.Archetypes
// Grid
int splits = 10;
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
var labelColor = style.ForegroundDisabled;
var labelFont = style.FontSmall;
//var blendArea = BlendAreaRect;
var blendArea = pointsArea;
for (int i = 0; i < splits; i++)
for (int i = 0; i <= splits; i++)
{
float x = blendArea.Left + blendArea.Width * i / splits;
Render2D.DrawLine(new Float2(x, 1), new Float2(x, rect.Height - 2), gridColor);
float alpha = (float)i / splits;
float x = blendArea.Left + blendArea.Width * alpha;
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
}
if (_is2D)
{
for (int i = 0; i < splits; i++)
var rangeY = new Float2(data0.Z, data0.W);
for (int i = 0; i <= splits; i++)
{
float y = blendArea.Top + blendArea.Height * i / splits;
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
float alpha = (float)i / splits;
float y = blendArea.Top + blendArea.Height * alpha;
float value = Mathf.Lerp(rangeY.X, rangeY.Y, alpha);
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
}
}
else
@@ -330,11 +587,24 @@ namespace FlaxEditor.Surface.Archetypes
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
}
// Base
base.Draw();
// Draw debug position
if (_debugPos.X > float.MinValue)
{
// Draw dot with outline
var icon = Editor.Instance.Icons.VisjectBoxOpen32;
var size = BlendPoint.DefaultSize;
var debugPos = BlendSpacePosToBlendPointPos(_debugPos);
var debugRect = new Rectangle(debugPos + new Float2(size * -0.5f) + size * 0.5f, new Float2(size));
var outline = Color.Black; // Shadow
Render2D.DrawSprite(icon, debugRect.MakeExpanded(2.0f), outline);
Render2D.DrawSprite(icon, debugRect, style.ProgressNormal);
}
// Frame
Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), containsFocus ? style.ProgressNormal : style.BackgroundSelected);
var frameColor = containsFocus ? style.BackgroundSelected : (IsMouseOver ? style.ForegroundGrey : style.ForegroundDisabled);
Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), frameColor);
}
}
@@ -346,6 +616,14 @@ namespace FlaxEditor.Surface.Archetypes
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
public abstract class MultiBlend : SurfaceNode
{
private Button _addButton;
private Button _removeButton;
/// <summary>
/// The blend space editor.
/// </summary>
protected BlendPointsEditor _editor;
/// <summary>
/// The selected animation label.
/// </summary>
@@ -379,7 +657,7 @@ namespace FlaxEditor.Surface.Archetypes
/// <summary>
/// The maximum animations amount to blend per node.
/// </summary>
public const int MaxAnimationsCount = 14;
public const int MaxAnimationsCount = 255;
/// <summary>
/// Gets or sets the index of the selected animation.
@@ -387,7 +665,12 @@ namespace FlaxEditor.Surface.Archetypes
public int SelectedAnimationIndex
{
get => _selectedAnimation.SelectedIndex;
set => _selectedAnimation.SelectedIndex = value;
set
{
OnSelectedAnimationPopupShowing(_selectedAnimation);
_selectedAnimation.SelectedIndex = value;
UpdateUI();
}
}
/// <inheritdoc />
@@ -402,20 +685,14 @@ namespace FlaxEditor.Surface.Archetypes
Text = "Selected Animation:",
Parent = this
};
_selectedAnimation = new ComboBox(_selectedAnimationLabel.X, 4 * layoutOffsetY, _selectedAnimationLabel.Width)
{
TooltipText = "Select blend point to view and edit it",
Parent = this
};
_selectedAnimation.PopupShowing += OnSelectedAnimationPopupShowing;
_selectedAnimation.SelectedIndexChanged += OnSelectedAnimationChanged;
var items = new List<string>(MaxAnimationsCount);
while (items.Count < MaxAnimationsCount)
items.Add(string.Empty);
_selectedAnimation.Items = items;
_animationPicker = new AssetPicker(new ScriptType(typeof(FlaxEngine.Animation)), new Float2(_selectedAnimation.Left, _selectedAnimation.Bottom + 4))
{
Parent = this
@@ -428,20 +705,36 @@ namespace FlaxEditor.Surface.Archetypes
Text = "Speed:",
Parent = this
};
_animationSpeed = new FloatValueBox(1.0f, _animationSpeedLabel.Right + 4, _animationSpeedLabel.Y, _selectedAnimation.Right - _animationSpeedLabel.Right - 4)
{
SlideSpeed = 0.01f,
Parent = this
};
_animationSpeed.ValueChanged += OnAnimationSpeedValueChanged;
var buttonsSize = 12;
_addButton = new Button(_selectedAnimation.Right - buttonsSize, _selectedAnimation.Bottom + 4, buttonsSize, buttonsSize)
{
Text = "+",
TooltipText = "Add a new blend point",
Parent = this
};
_addButton.Clicked += OnAddButtonClicked;
_removeButton = new Button(_addButton.Left - buttonsSize - 4, _addButton.Y, buttonsSize, buttonsSize)
{
Text = "-",
TooltipText = "Remove selected blend point",
Parent = this
};
_removeButton.Clicked += OnRemoveButtonClicked;
}
private void OnSelectedAnimationPopupShowing(ComboBox comboBox)
{
var items = comboBox.Items;
items.Clear();
for (var i = 0; i < MaxAnimationsCount; i++)
var count = _editor.PointsCount;
for (var i = 0; i < count; i++)
{
var animId = (Guid)Values[5 + i * 2];
var path = string.Empty;
@@ -484,6 +777,16 @@ namespace FlaxEditor.Surface.Archetypes
}
}
private void OnAddButtonClicked()
{
_editor.AddPoint();
}
private void OnRemoveButtonClicked()
{
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
}
/// <summary>
/// Updates the editor UI.
/// </summary>
@@ -491,7 +794,7 @@ namespace FlaxEditor.Surface.Archetypes
/// <param name="isValid">if set to <c>true</c> is selection valid.</param>
/// <param name="data0">The packed data 0.</param>
/// <param name="data1">The packed data 1.</param>
protected virtual void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
public virtual void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
{
if (isValid)
{
@@ -511,19 +814,21 @@ namespace FlaxEditor.Surface.Archetypes
_animationPicker.Enabled = isValid;
_animationSpeedLabel.Enabled = isValid;
_animationSpeed.Enabled = isValid;
_addButton.Enabled = _editor.PointsCount < MaxAnimationsCount;
_removeButton.Enabled = isValid && data1 != Guid.Empty;
}
/// <summary>
/// Updates the editor UI.
/// </summary>
protected void UpdateUI()
public void UpdateUI()
{
if (_isUpdatingUI)
return;
_isUpdatingUI = true;
var selectedIndex = _selectedAnimation.SelectedIndex;
var isValid = selectedIndex != -1;
var isValid = selectedIndex >= 0 && selectedIndex < _editor.PointsCount;
Float4 data0;
Guid data1;
if (isValid)
@@ -549,6 +854,16 @@ namespace FlaxEditor.Surface.Archetypes
UpdateUI();
}
/// <inheritdoc />
public override void OnSpawned(SurfaceNodeActions action)
{
base.OnSpawned(action);
// Select the first animation to make setup easier
OnSelectedAnimationPopupShowing(_selectedAnimation);
_selectedAnimation.SelectedIndex = 0;
}
/// <inheritdoc />
public override void OnValuesChanged()
{
@@ -566,67 +881,6 @@ namespace FlaxEditor.Surface.Archetypes
{
private readonly Label _animationXLabel;
private readonly FloatValueBox _animationX;
private readonly Editor _editor;
/// <summary>
/// The Multi Blend 1D blend space editor.
/// </summary>
/// <seealso cref="FlaxEditor.Surface.Archetypes.BlendPointsEditor" />
protected class Editor : BlendPointsEditor
{
private MultiBlend1D _node;
/// <summary>
/// Initializes a new instance of the <see cref="Editor"/> class.
/// </summary>
/// <param name="node">The parent Visject Node node.</param>
/// <param name="x">The X location.</param>
/// <param name="y">The Y location.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public Editor(MultiBlend1D node, float x, float y, float width, float height)
: base(false, x, y, width, height)
{
_node = node;
}
/// <inheritdoc />
public override void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations)
{
var data0 = (Float4)_node.Values[0];
rangeX = new Float2(data0.X, data0.Y);
rangeY = Float2.Zero;
for (int i = 0; i < MaxAnimationsCount; i++)
{
var dataA = (Float4)_node.Values[4 + i * 2];
var dataB = (Guid)_node.Values[5 + i * 2];
pointsAnims[i] = dataB;
pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), 0.0f);
}
}
/// <inheritdoc />
public override int SelectedIndex
{
get => _node.SelectedAnimationIndex;
set => _node.SelectedAnimationIndex = value;
}
/// <inheritdoc />
public override void SetLocation(int index, Float2 location)
{
var dataA = (Float4)_node.Values[4 + index * 2];
var ranges = (Float4)_node.Values[0];
dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y);
_node.Values[4 + index * 2] = dataA;
_node.Surface.MarkAsEdited();
_node.UpdateUI();
}
}
/// <inheritdoc />
public MultiBlend1D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
@@ -646,11 +900,8 @@ namespace FlaxEditor.Surface.Archetypes
};
_animationX.ValueChanged += OnAnimationXChanged;
_editor = new Editor(this,
FlaxEditor.Surface.Constants.NodeMarginX,
_animationX.Bottom + 4.0f,
Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f,
120.0f);
var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f;
_editor = new BlendPointsEditor(this, false, FlaxEditor.Surface.Constants.NodeMarginX, _animationX.Bottom + 4.0f, size, 120.0f);
_editor.Parent = this;
}
@@ -670,7 +921,7 @@ namespace FlaxEditor.Surface.Archetypes
}
/// <inheritdoc />
protected override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
{
base.UpdateUI(selectedIndex, isValid, ref data0, ref data1);
@@ -700,68 +951,6 @@ namespace FlaxEditor.Surface.Archetypes
private readonly FloatValueBox _animationX;
private readonly Label _animationYLabel;
private readonly FloatValueBox _animationY;
private readonly Editor _editor;
/// <summary>
/// The Multi Blend 2D blend space editor.
/// </summary>
/// <seealso cref="FlaxEditor.Surface.Archetypes.BlendPointsEditor" />
protected class Editor : BlendPointsEditor
{
private MultiBlend2D _node;
/// <summary>
/// Initializes a new instance of the <see cref="Editor"/> class.
/// </summary>
/// <param name="node">The parent Visject Node node.</param>
/// <param name="x">The X location.</param>
/// <param name="y">The Y location.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public Editor(MultiBlend2D node, float x, float y, float width, float height)
: base(true, x, y, width, height)
{
_node = node;
}
/// <inheritdoc />
public override void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations)
{
var data0 = (Float4)_node.Values[0];
rangeX = new Float2(data0.X, data0.Y);
rangeY = new Float2(data0.Z, data0.W);
for (int i = 0; i < MaxAnimationsCount; i++)
{
var dataA = (Float4)_node.Values[4 + i * 2];
var dataB = (Guid)_node.Values[5 + i * 2];
pointsAnims[i] = dataB;
pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), Mathf.Clamp(dataA.Y, rangeY.X, rangeY.Y));
}
}
/// <inheritdoc />
public override int SelectedIndex
{
get => _node.SelectedAnimationIndex;
set => _node.SelectedAnimationIndex = value;
}
/// <inheritdoc />
public override void SetLocation(int index, Float2 location)
{
var dataA = (Float4)_node.Values[4 + index * 2];
var ranges = (Float4)_node.Values[0];
dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y);
dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W);
_node.Values[4 + index * 2] = dataA;
_node.Surface.MarkAsEdited();
_node.UpdateUI();
}
}
/// <inheritdoc />
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
@@ -795,11 +984,8 @@ namespace FlaxEditor.Surface.Archetypes
};
_animationY.ValueChanged += OnAnimationYChanged;
_editor = new Editor(this,
FlaxEditor.Surface.Constants.NodeMarginX,
_animationY.Bottom + 4.0f,
Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f,
120.0f);
var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f;
_editor = new BlendPointsEditor(this, true, FlaxEditor.Surface.Constants.NodeMarginX, _animationY.Bottom + 4.0f, size, size);
_editor.Parent = this;
}
@@ -834,7 +1020,7 @@ namespace FlaxEditor.Surface.Archetypes
}
/// <inheritdoc />
protected override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
{
base.UpdateUI(selectedIndex, isValid, ref data0, ref data1);
+15 -41
View File
@@ -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
{
+5
View File
@@ -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>
+25 -9
View File
@@ -951,15 +951,20 @@ namespace FlaxEditor.Surface
{
if (_isDuringValuesEditing || !Surface.CanEdit)
return;
if (values == null || Values == null || values.Length != Values.Length)
if (values == null || Values == null)
throw new ArgumentException();
bool resize = values.Length != Values.Length;
if (resize && (Archetype.Flags & NodeFlags.VariableValuesSize) == 0)
throw new ArgumentException();
_isDuringValuesEditing = true;
var before = Surface.Undo != null ? (object[])Values.Clone() : null;
Array.Copy(values, Values, values.Length);
if (resize)
Values = (object[])values.Clone();
else
Array.Copy(values, Values, values.Length);
OnValuesChanged();
Surface.MarkAsEdited(graphEdited);
@@ -1057,6 +1062,20 @@ namespace FlaxEditor.Surface
}
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (base.OnMouseDown(location, button))
return true;
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
return true;
if (button == MouseButton.Right)
return true;
return false;
}
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
@@ -1064,13 +1083,10 @@ namespace FlaxEditor.Surface
return true;
// Close
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0)
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
{
if (_closeButtonRect.Contains(ref location))
{
Surface.Delete(this);
return true;
}
Surface.Delete(this);
return true;
}
// Secondary Context Menu
@@ -14,6 +14,7 @@ namespace FlaxEditor.Surface.Undo
private ContextHandle _context;
private readonly uint _nodeId;
private readonly bool _graphEdited;
private readonly bool _resize;
private object[] _before;
private object[] _after;
@@ -23,7 +24,8 @@ namespace FlaxEditor.Surface.Undo
throw new ArgumentNullException(nameof(before));
if (node?.Values == null)
throw new ArgumentNullException(nameof(node));
if (before.Length != node.Values.Length)
_resize = before.Length != node.Values.Length;
if (_resize && (node.Archetype.Flags & NodeFlags.VariableValuesSize) == 0)
throw new ArgumentException(nameof(before));
_surface = node.Surface;
@@ -48,7 +50,10 @@ namespace FlaxEditor.Surface.Undo
throw new Exception("Missing node.");
node.SetIsDuringValuesEditing(true);
Array.Copy(_after, node.Values, _after.Length);
if (_resize)
node.Values = (object[])_after.Clone();
else
Array.Copy(_after, node.Values, _after.Length);
node.OnValuesChanged();
context.MarkAsModified(_graphEdited);
node.SetIsDuringValuesEditing(false);
@@ -65,7 +70,10 @@ namespace FlaxEditor.Surface.Undo
throw new Exception("Missing node.");
node.SetIsDuringValuesEditing(true);
Array.Copy(_before, node.Values, _before.Length);
if (_resize)
node.Values = (object[])_before.Clone();
else
Array.Copy(_before, node.Values, _before.Length);
node.OnValuesChanged();
context.MarkAsModified(_graphEdited);
node.SetIsDuringValuesEditing(false);
+25 -13
View File
@@ -17,6 +17,11 @@ namespace FlaxEditor.Surface
/// </summary>
public readonly InputActionsContainer InputActions;
/// <summary>
/// Optional feature.
/// </summary>
public bool PanWithMiddleMouse = false;
private string _currentInputText = string.Empty;
private Float2 _movingNodesDelta;
private HashSet<SurfaceNode> _movingNodes;
@@ -223,15 +228,18 @@ namespace FlaxEditor.Surface
if (_middleMouseDown)
{
// Calculate delta
var delta = location - _middleMouseDownPos;
if (delta.LengthSquared > 0.01f)
if (PanWithMiddleMouse)
{
// Move view
_mouseMoveAmount += delta.Length;
_rootControl.Location += delta;
_middleMouseDownPos = location;
Cursor = CursorType.SizeAll;
// Calculate delta
var delta = location - _middleMouseDownPos;
if (delta.LengthSquared > 0.01f)
{
// Move view
_mouseMoveAmount += delta.Length;
_rootControl.Location += delta;
_middleMouseDownPos = location;
Cursor = CursorType.SizeAll;
}
}
// Handled
@@ -300,7 +308,8 @@ namespace FlaxEditor.Surface
if (_middleMouseDown)
{
_middleMouseDown = false;
Cursor = CursorType.Default;
if (PanWithMiddleMouse)
Cursor = CursorType.Default;
}
_isMovingSelection = false;
ConnectingEnd(null);
@@ -483,7 +492,7 @@ namespace FlaxEditor.Surface
Focus();
return true;
}
if (_rightMouseDown || _middleMouseDown)
if (_rightMouseDown || (_middleMouseDown && _middleMouseDown))
{
// Start navigating
StartMouseCapture();
@@ -555,9 +564,12 @@ namespace FlaxEditor.Surface
if (_middleMouseDown && button == MouseButton.Middle)
{
_middleMouseDown = false;
EndMouseCapture();
Cursor = CursorType.Default;
if (_mouseMoveAmount > 0)
if (_middleMouseDown)
{
EndMouseCapture();
Cursor = CursorType.Default;
}
if (_mouseMoveAmount > 0 && _middleMouseDown)
_mouseMoveAmount = 0;
else if (CanEdit)
{
@@ -630,6 +630,15 @@ namespace FlaxEditor.Surface
stream.ReadCommonValue(ref node.Values[j]);
}
}
else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
{
node.Values = new object[valuesCnt];
for (int j = firstValueReadIdx; j < valuesCnt; j++)
{
// ReSharper disable once PossibleNullReferenceException
stream.ReadCommonValue(ref node.Values[j]);
}
}
else
{
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
@@ -795,6 +804,12 @@ namespace FlaxEditor.Surface
for (int j = firstValueReadIdx; j < valuesCnt; j++)
node.Values[j] = stream.ReadVariant();
}
else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
{
node.Values = new object[valuesCnt];
for (int j = firstValueReadIdx; j < valuesCnt; j++)
node.Values[j] = stream.ReadVariant();
}
else
{
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
@@ -66,8 +66,8 @@ namespace FlaxEditor.Tools.Terrain
[EditorOrder(410), EditorDisplay("Transform", "Rotation"), DefaultValue(typeof(Quaternion), "0,0,0,1"), Tooltip("Orientation of the terrain")]
public Quaternion Orientation = Quaternion.Identity;
[EditorOrder(420), EditorDisplay("Transform", "Scale"), DefaultValue(typeof(Float3), "1,1,1"), Limit(float.MinValue, float.MaxValue, 0.01f), Tooltip("Scale of the terrain")]
public Float3 Scale = Float3.One;
[EditorOrder(420), EditorDisplay("Transform", "Scale"), DefaultValue(1.0f), Limit(0.0001f, float.MaxValue, 0.01f), Tooltip("Scale of the terrain")]
public float Scale = 1.0f;
}
private readonly Options _options = new Options();
@@ -147,7 +147,7 @@ namespace FlaxEditor.Tools.Terrain
// Create terrain object and setup some options
var terrain = new FlaxEngine.Terrain();
terrain.Setup(_options.LODCount, (int)_options.ChunkSize);
terrain.Transform = new Transform(_options.Position, _options.Orientation, _options.Scale);
terrain.Transform = new Transform(_options.Position, _options.Orientation, new Float3(_options.Scale));
terrain.Material = _options.Material;
terrain.CollisionLOD = _options.CollisionLOD;
if (_options.Heightmap)
@@ -238,24 +238,5 @@ namespace FlaxEditor.Tools.Terrain
return base.CanCloseWindow(reason);
}
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
if (_isWorking)
return true;
switch (key)
{
case KeyboardKeys.Escape:
OnCancel();
return true;
case KeyboardKeys.Return:
OnSubmit();
return true;
}
return base.OnKeyDown(key);
}
}
}
+4 -7
View File
@@ -80,7 +80,7 @@ struct TextureDataResult
}
};
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data)
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false)
{
// Lock asset chunks (if not virtual)
data.Lock = texture->LockData();
@@ -103,7 +103,7 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data)
// Decompress or convert data if need to
data.Mip0DataPtr = &data.Mip0Data;
if (PixelFormatExtensions::IsCompressed(data.Format))
if (PixelFormatExtensions::IsCompressed(data.Format) || TextureTool::GetSampler(data.Format) == nullptr)
{
PROFILE_CPU_NAMED("Decompress");
@@ -122,7 +122,7 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data)
srcMip.Lines = src.Height;
// Decompress texture
if (TextureTool::Convert(data.Tmp, src, PixelFormat::R8G8B8A8_UNorm))
if (TextureTool::Convert(data.Tmp, src, hdr ? PixelFormat::R16G16B16A16_Float : PixelFormat::R8G8B8A8_UNorm))
{
LOG(Warning, "Failed to decompress data.");
return true;
@@ -134,7 +134,6 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data)
data.SlicePitch = data.Tmp.Items[0].Mips[0].DepthPitch;
data.Mip0DataPtr = &data.Tmp.Items[0].Mips[0].Data;
}
// TODO: convert to RGBA from other formats that cannot be sampled?
// Check if can even sample the given format
const auto sampler = TextureTool::GetSampler(data.Format);
@@ -155,7 +154,6 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
LOG(Warning, "Cannot setup terain with no patches.");
return false;
}
PROFILE_CPU_NAMED("Terrain.GenerateTerrain");
// Wait for assets to be loaded
@@ -188,7 +186,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
{
// Get data
TextureDataResult dataHeightmap;
if (GetTextureDataForSampling(heightmap, dataHeightmap))
if (GetTextureDataForSampling(heightmap, dataHeightmap, true))
return true;
const auto sampler = TextureTool::GetSampler(dataHeightmap.Format);
@@ -198,7 +196,6 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
{
auto patch = terrain->GetPatch(patchIndex);
const Vector2 uvStart = Vector2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
// Sample heightmap pixels with interpolation to get actual heightmap vertices locations
+4 -2
View File
@@ -120,9 +120,9 @@ namespace FlaxEditor.Utilities
/// <summary>
/// Deletes the creates scenes for the simulation.
/// </summary>
public void DeletedScenes()
public void UnloadScenes()
{
Profiler.BeginEvent("DuplicateScenes.DeletedScenes");
Profiler.BeginEvent("DuplicateScenes.UnloadScenes");
Editor.Log("Restoring scene data");
// TODO: here we can keep changes for actors marked to keep their state after simulation
@@ -134,6 +134,8 @@ namespace FlaxEditor.Utilities
throw new Exception("Failed to unload scenes.");
}
FlaxEngine.Scripting.FlushRemovedObjects();
Editor.WipeOutLeftoverSceneObjects();
Profiler.EndEvent();
}
+5
View File
@@ -210,12 +210,17 @@ namespace FlaxEditor.Utilities
/// <returns>The duplicated value.</returns>
internal static object CloneValue(object value)
{
// For object references just clone it
if (value is FlaxEngine.Object)
return value;
// For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor
if (value != null && (!value.GetType().IsValueType || !value.GetType().IsClass))
{
var json = JsonSerializer.Serialize(value);
value = JsonSerializer.Deserialize(json, value.GetType());
}
return value;
}
@@ -76,7 +76,7 @@ namespace FlaxEditor.Viewport
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
/// <inheritdoc />
public Float2 MouseDelta => _mouseDelta * 1000;
public Float2 MouseDelta => _mouseDelta;
/// <inheritdoc />
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
+16 -9
View File
@@ -65,6 +65,11 @@ namespace FlaxEditor.Viewport
/// </summary>
public bool IsAltDown;
/// <summary>
/// The is alt down flag cached from the previous input. Used to make <see cref="IsControllingMouse"/> consistent when user releases Alt while orbiting with Alt+LMB.
/// </summary>
public bool WasAltDownBefore;
/// <summary>
/// The is mouse right down flag.
/// </summary>
@@ -88,18 +93,20 @@ namespace FlaxEditor.Viewport
/// <summary>
/// Gets a value indicating whether use is controlling mouse.
/// </summary>
public bool IsControllingMouse => IsMouseMiddleDown || IsMouseRightDown || (IsAltDown && IsMouseLeftDown) || Mathf.Abs(MouseWheelDelta) > 0.1f;
public bool IsControllingMouse => IsMouseMiddleDown || IsMouseRightDown || ((IsAltDown || WasAltDownBefore) && IsMouseLeftDown) || Mathf.Abs(MouseWheelDelta) > 0.1f;
/// <summary>
/// Gathers input from the specified window.
/// </summary>
/// <param name="window">The window.</param>
/// <param name="useMouse">True if use mouse input, otherwise will skip mouse.</param>
public void Gather(Window window, bool useMouse)
/// <param name="prevInput">Previous input state.</param>
public void Gather(Window window, bool useMouse, ref Input prevInput)
{
IsControlDown = window.GetKey(KeyboardKeys.Control);
IsShiftDown = window.GetKey(KeyboardKeys.Shift);
IsAltDown = window.GetKey(KeyboardKeys.Alt);
WasAltDownBefore = prevInput.WasAltDownBefore || prevInput.IsAltDown;
IsMouseRightDown = useMouse && window.GetMouseButton(MouseButton.Right);
IsMouseMiddleDown = useMouse && window.GetMouseButton(MouseButton.Middle);
@@ -114,6 +121,7 @@ namespace FlaxEditor.Viewport
IsControlDown = false;
IsShiftDown = false;
IsAltDown = false;
WasAltDownBefore = false;
IsMouseRightDown = false;
IsMouseMiddleDown = false;
@@ -998,6 +1006,7 @@ namespace FlaxEditor.Viewport
InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown);
InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1));
InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1));
InputActions.Add(options => options.ToggleOrthographic, () => OnOrthographicModeToggled(null));
// Link for task event
task.Begin += OnRenderBegin;
@@ -1539,7 +1548,7 @@ namespace FlaxEditor.Viewport
_prevInput = _input;
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
if (canUseInput && ContainsFocus && hit == null)
_input.Gather(win.Window, useMouse);
_input.Gather(win.Window, useMouse, ref _prevInput);
else
_input.Clear();
@@ -1662,8 +1671,7 @@ namespace FlaxEditor.Viewport
{
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
_mouseDelta = offset / size;
_mouseDelta.Y *= size.Y / size.X;
_mouseDelta = offset;
// Update delta filtering buffer
_deltaFilteringBuffer[_deltaFilteringStep] = _mouseDelta;
@@ -1681,8 +1689,7 @@ namespace FlaxEditor.Viewport
}
else
{
_mouseDelta = offset / size;
_mouseDelta.Y *= size.Y / size.X;
_mouseDelta = offset;
mouseDelta = _mouseDelta;
}
@@ -1696,7 +1703,7 @@ namespace FlaxEditor.Viewport
// Update
moveDelta *= dt * (60.0f * 4.0f);
mouseDelta *= 200.0f * MouseSpeed * _mouseSensitivity;
mouseDelta *= 0.1833f * MouseSpeed * _mouseSensitivity;
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
// Move mouse back to the root position
@@ -1722,7 +1729,7 @@ namespace FlaxEditor.Viewport
var offset = _viewMousePos - _startPos;
offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X);
offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y);
_mouseDelta = offset / size;
_mouseDelta = offset;
_startPos = _viewMousePos;
}
else
@@ -291,7 +291,7 @@ namespace FlaxEditor.Viewport
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
/// <inheritdoc />
public Float2 MouseDelta => _mouseDelta * 1000;
public Float2 MouseDelta => _mouseDelta;
/// <inheritdoc />
public bool UseSnapping => Root.GetKey(KeyboardKeys.Control);
@@ -336,7 +336,8 @@ namespace FlaxEditor.Viewport.Previews
if (_showNodes)
{
// Draw bounding box at the node locations
var localBox = new OrientedBoundingBox(new Vector3(-1.0f), new Vector3(1.0f));
var boxSize = Mathf.Min(1.0f, _previewModel.Sphere.Radius / 100.0f);
var localBox = new OrientedBoundingBox(new Vector3(-boxSize), new Vector3(boxSize));
for (int nodeIndex = 0; nodeIndex < pose.Length; nodeIndex++)
{
if (nodesMask != null && !nodesMask[nodeIndex])
@@ -115,7 +115,12 @@ namespace FlaxEditor.Viewport.Previews
_hasUILinked = true;
}
else if (_uiControlLinked != null)
{
if (_uiControlLinked.Control != null &&
_uiControlLinked.Control.Parent == null)
_uiControlLinked.Control.Parent = _uiParentLink;
_hasUILinked = true;
}
}
private void LinkCanvas(Actor actor)
@@ -494,6 +494,7 @@ namespace FlaxEditor.Windows.Assets
_undo.Enabled = false;
_undo.Clear();
_propertiesEditor.BuildLayoutOnUpdate();
UpdateToolstrip();
}
/// <inheritdoc />
@@ -504,6 +505,7 @@ namespace FlaxEditor.Windows.Assets
_undo.Enabled = true;
_undo.Clear();
_propertiesEditor.BuildLayoutOnUpdate();
UpdateToolstrip();
}
/// <inheritdoc />
@@ -54,6 +54,9 @@ namespace FlaxEditor.Windows.Assets
/// <param name="before">The selection before the change.</param>
public void OnSelectionChanged(SceneGraphNode[] before)
{
if (LockSelectedObjects)
return;
Undo.AddAction(new SelectionChangeAction(before, Selection.ToArray(), OnSelectionUndo));
OnSelectionChanges();
@@ -68,6 +68,11 @@ namespace FlaxEditor.Windows.Assets
/// </summary>
public readonly LocalSceneGraph Graph;
/// <summary>
/// Indication of if the prefab window selection is locked on specific objects.
/// </summary>
public bool LockSelectedObjects = false;
/// <summary>
/// Gets or sets a value indicating whether use live reloading for the prefab changes (applies prefab changes on modification by auto).
/// </summary>
+9
View File
@@ -665,7 +665,16 @@ namespace FlaxEditor.Windows
// Scroll to the new entry (if any added to view)
if (scrollView && anyVisible)
{
panelScroll.ScrollViewTo(newEntry);
bool scrollViewNew = (panelScroll.VScrollBar.Maximum - panelScroll.VScrollBar.TargetValue) < LogEntry.DefaultHeight * 1.5f;
if (scrollViewNew != scrollView)
{
// Make sure scrolling doesn't stop in case too many entries were added at once
panelScroll.ScrollViewTo(new Float2(float.MaxValue, float.MaxValue));
}
}
}
}
@@ -37,6 +37,11 @@ namespace FlaxEditor.Windows
/// </summary>
public bool UIPivotRelative = true;
/// <summary>
/// Indication of if the properties window is locked on specific objects.
/// </summary>
public bool LockObjects = false;
/// <summary>
/// Initializes a new instance of the <see cref="PropertiesWindow"/> class.
/// </summary>
@@ -62,6 +67,9 @@ namespace FlaxEditor.Windows
private void OnSelectionChanged()
{
if (LockObjects)
return;
// Update selected objects
// TODO: use cached collection for less memory allocations
undoRecordObjects = Editor.SceneEditing.Selection.ConvertAll(x => x.UndoRecordObject).Distinct();
+9
View File
@@ -196,6 +196,7 @@ namespace FlaxEditor.Windows
{
if (actorType.IsAbstract)
continue;
_groupSearch.AddChild(CreateActorItem(Utilities.Utils.GetPropertyNameUI(actorType.Name), actorType));
ActorToolboxAttribute attribute = null;
foreach (var e in actorType.GetAttributes(false))
{
@@ -235,6 +236,7 @@ namespace FlaxEditor.Windows
group.AddChild(string.IsNullOrEmpty(attribute.Name) ? CreateActorItem(Utilities.Utils.GetPropertyNameUI(actorType.Name), actorType) : CreateActorItem(attribute.Name, actorType));
group.SortChildren();
}
_groupSearch.SortChildren();
}
private void OnSearchBoxTextChanged()
@@ -260,6 +262,10 @@ namespace FlaxEditor.Windows
}
var text = (attribute == null) ? actorType.Name : string.IsNullOrEmpty(attribute.Name) ? actorType.Name : attribute.Name;
// Display all actors on no search
if (string.IsNullOrEmpty(filterText))
_groupSearch.AddChild(CreateActorItem(Utilities.Utils.GetPropertyNameUI(text), actorType));
if (!QueryFilterHelper.Match(filterText, text, out QueryFilterHelper.Range[] ranges))
continue;
@@ -278,6 +284,9 @@ namespace FlaxEditor.Windows
}
item.SetHighlights(highlights);
}
if (string.IsNullOrEmpty(filterText))
_groupSearch.SortChildren();
_groupSearch.UnlockChildrenRecursive();
PerformLayout();
+17 -1
View File
@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/Pair.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Animations/Curve.h"
@@ -68,6 +69,16 @@ public:
uint64 GetMemoryUsage() const;
};
/// <summary>
/// Single track with events.
/// </summary>
struct EventAnimationData
{
float Duration = 0.0f;
StringAnsi TypeName;
StringAnsi JsonData;
};
/// <summary>
/// Root Motion modes that can be applied by the animation. Used as flags for selective behavior.
/// </summary>
@@ -120,10 +131,15 @@ struct AnimationData
String RootNodeName;
/// <summary>
/// The per skeleton node animation channels.
/// The per-skeleton node animation channels.
/// </summary>
Array<NodeAnimationData> Channels;
/// <summary>
/// The animation event tracks.
/// </summary>
Array<Pair<String, StepCurve<EventAnimationData>>> Events;
public:
/// <summary>
/// Gets the length of the animation (in seconds).
+1 -1
View File
@@ -35,7 +35,7 @@ public:
namespace
{
FORCE_INLINE bool CanUpdateModel(AnimatedModel* animatedModel)
FORCE_INLINE bool CanUpdateModel(const AnimatedModel* animatedModel)
{
auto skinnedModel = animatedModel->SkinnedModel.Get();
auto animGraph = animatedModel->AnimationGraph.Get();
@@ -117,11 +117,11 @@ void InstanceDataBucketInit(AnimGraphInstanceData::Bucket& bucket)
bucket.InstanceData.Init = true;
}
bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
bool SortMultiBlend1D(const ANIM_GRAPH_MULTI_BLEND_INDEX& a, const ANIM_GRAPH_MULTI_BLEND_INDEX& b, AnimGraphNode* n)
{
// Sort items by X location from the lowest to the highest
const auto aX = a == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? MAX_float : n->Values[4 + a * 2].AsFloat4().X;
const auto bX = b == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? MAX_float : n->Values[4 + b * 2].AsFloat4().X;
const auto aX = a == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + a * 2].AsFloat4().X;
const auto bX = b == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + b * 2].AsFloat4().X;
return aX < bX;
}
@@ -133,8 +133,6 @@ bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
bool AnimGraphBase::onNodeLoaded(Node* n)
{
((AnimGraphNode*)n)->Graph = _graph;
// Check if this node needs a state container
switch (n->GroupID)
{
// Tools
@@ -163,53 +161,51 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
// Animation
case 2:
ADD_BUCKET(AnimationBucketInit);
n->Assets.Resize(1);
n->Assets[0] = (Asset*)Content::LoadAsync<Animation>((Guid)n->Values[0]);
break;
// Blend with Mask
case 11:
n->Assets.Resize(1);
n->Assets[0] = (Asset*)Content::LoadAsync<SkeletonMask>((Guid)n->Values[1]);
break;
// Multi Blend 1D
case 12:
{
ADD_BUCKET(MultiBlendBucketInit);
n->Data.MultiBlend1D.Count = (ANIM_GRAPH_MULTI_BLEND_INDEX)((n->Values.Count() - 4) / 2); // 4 node values + 2 per blend point
n->Data.MultiBlend1D.Length = -1;
const Float4 range = n->Values[0].AsFloat4();
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
n->Data.MultiBlend1D.IndicesSorted = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX) * n->Data.MultiBlend1D.Count);
n->Assets.Resize(n->Data.MultiBlend1D.Count);
for (int32 i = 0; i < n->Data.MultiBlend1D.Count; i++)
{
n->Assets[i] = Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
n->Data.MultiBlend1D.IndicesSorted[i] = n->Assets[i] ? i : ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS;
n->Data.MultiBlend1D.IndicesSorted[i] = (ANIM_GRAPH_MULTI_BLEND_INDEX)(n->Assets[i] ? i : ANIM_GRAPH_MULTI_BLEND_INVALID);
}
Sorting::SortArray(n->Data.MultiBlend1D.IndicesSorted, ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS, &SortMultiBlend1D, n);
Sorting::SortArray(n->Data.MultiBlend1D.IndicesSorted, n->Data.MultiBlend1D.Count, &SortMultiBlend1D, n);
break;
}
// Multi Blend 2D
case 13:
{
ADD_BUCKET(MultiBlendBucketInit);
n->Data.MultiBlend1D.Count = (ANIM_GRAPH_MULTI_BLEND_INDEX)((n->Values.Count() - 4) / 2); // 4 node values + 2 per blend point
n->Data.MultiBlend2D.Length = -1;
// Get blend points locations
Array<Float2, FixedAllocation<ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS + 3>> vertices;
byte vertexIndexToAnimIndex[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS];
const Float4 range = n->Values[0].AsFloat4();
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
Array<Float2> vertices;
Array<ANIM_GRAPH_MULTI_BLEND_INDEX> vertexToAnim;
n->Assets.Resize(n->Data.MultiBlend1D.Count);
for (int32 i = 0; i < n->Data.MultiBlend1D.Count; i++)
{
n->Assets[i] = (Asset*)Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
n->Assets[i] = Content::LoadAsync<Animation>((Guid)n->Values[i * 2 + 5]);
if (n->Assets[i])
{
const int32 vertexIndex = vertices.Count();
vertexIndexToAnimIndex[vertexIndex] = i;
vertices.Add(Float2(n->Values[i * 2 + 4].AsFloat4()));
}
else
{
vertexIndexToAnimIndex[i] = -1;
vertexToAnim.Add((ANIM_GRAPH_MULTI_BLEND_INDEX)i);
}
}
// Triangulate
Array<Delaunay2D::Triangle, FixedAllocation<ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS>> triangles;
Array<Delaunay2D::Triangle> triangles;
Delaunay2D::Triangulate(vertices, triangles);
if (triangles.Count() == 0)
{
@@ -227,15 +223,14 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
}
// Store triangles vertices indices (map the back to the anim node slots)
for (int32 i = 0; i < triangles.Count(); i++)
n->Data.MultiBlend2D.TrianglesCount = triangles.Count();
n->Data.MultiBlend2D.Triangles = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(triangles.Count() * 3 - sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX));
for (int32 i = 0, t = 0; i < triangles.Count(); i++)
{
n->Data.MultiBlend2D.TrianglesP0[i] = vertexIndexToAnimIndex[triangles[i].Indices[0]];
n->Data.MultiBlend2D.TrianglesP1[i] = vertexIndexToAnimIndex[triangles[i].Indices[1]];
n->Data.MultiBlend2D.TrianglesP2[i] = vertexIndexToAnimIndex[triangles[i].Indices[2]];
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[0]];
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[1]];
n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[2]];
}
if (triangles.Count() < ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS)
n->Data.MultiBlend2D.TrianglesP0[triangles.Count()] = ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS;
break;
}
// Blend Pose
@@ -307,6 +302,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
data.Graph = nullptr;
break;
}
n->Assets.Resize(1);
n->Assets[0] = function;
// Load the graph
@@ -384,6 +380,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Value& transitionsData)
{
int32 validTransitions = 0;
data.Transitions = nullptr;
if (transitionsData.Type == VariantType::Blob && transitionsData.AsBlob.Length)
{
MemoryReadStream stream((byte*)transitionsData.AsBlob.Data, transitionsData.AsBlob.Length);
@@ -398,8 +395,11 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val
int32 transitionsCount;
stream.ReadInt32(&transitionsCount);
if (transitionsCount == 0)
return;
StateTransitions.EnsureCapacity(StateTransitions.Count() + transitionsCount);
data.Transitions = (uint16*)Allocator::Allocate(transitionsCount * sizeof(uint16));
AnimGraphStateTransition transition;
for (int32 i = 0; i < transitionsCount; i++)
@@ -434,7 +434,6 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val
// Skip disabled transitions
continue;
}
if (ruleSize != 0)
{
transition.RuleGraph = LoadSubGraph(ruleBytes, ruleSize, TEXT("Rule"));
@@ -444,25 +443,19 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val
continue;
}
}
if (transition.Destination == nullptr)
{
LOG(Warning, "Missing target node for the state machine transition.");
continue;
}
if (validTransitions == ANIM_GRAPH_MAX_STATE_TRANSITIONS)
{
LOG(Warning, "State uses too many transitions.");
continue;
}
data.Transitions[validTransitions++] = (uint16)StateTransitions.Count();
StateTransitions.Add(transition);
}
}
if (validTransitions != ANIM_GRAPH_MAX_STATE_TRANSITIONS)
// Last entry is invalid to indicate end
data.Transitions[validTransitions] = AnimGraphNode::StateData::InvalidTransitionIndex;
}
// Release data to don't use that memory
transitionsData = AnimGraphExecutor::Value::Null;
@@ -102,6 +102,34 @@ AnimGraphInstanceData::OutgoingEvent AnimGraphInstanceData::ActiveEvent::End(Ani
return out;
}
AnimGraphNode::~AnimGraphNode()
{
// Free allocated memory
switch (GroupID)
{
// Animation
case 9:
switch (TypeID)
{
// Multi Blend 1D
case 12:
Allocator::Free(Data.MultiBlend1D.IndicesSorted);
break;
// Multi Blend 2D
case 13:
Allocator::Free(Data.MultiBlend2D.Triangles);
break;
// State
case 20:
// Any State
case 34:
Allocator::Free(Data.State.Transitions);
break;
}
break;
}
}
AnimGraphImpulse* AnimGraphNode::GetNodes(AnimGraphExecutor* executor)
{
auto& context = *AnimGraphExecutor::Context.Get();
+24 -56
View File
@@ -5,6 +5,7 @@
#include "Engine/Visject/VisjectGraph.h"
#include "Engine/Content/Assets/Animation.h"
#include "Engine/Core/Collections/ChunkedArray.h"
#include "Engine/Core/Collections/BitArray.h"
#include "Engine/Animations/AlphaBlend.h"
#include "Engine/Core/Math/Matrix.h"
#include "../Config.h"
@@ -12,9 +13,8 @@
#define ANIM_GRAPH_PARAM_BASE_MODEL_ID Guid(1000, 0, 0, 0)
#define ANIM_GRAPH_IS_VALID_PTR(value) (value.Type.Type == VariantType::Pointer && value.AsPointer != nullptr)
#define ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS 14
#define ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS 32
#define ANIM_GRAPH_MAX_STATE_TRANSITIONS 64
#define ANIM_GRAPH_MULTI_BLEND_INDEX byte
#define ANIM_GRAPH_MULTI_BLEND_INVALID 0xff
#define ANIM_GRAPH_MAX_CALL_STACK 100
#define ANIM_GRAPH_MAX_EVENTS 64
@@ -426,61 +426,31 @@ struct AnimGraphTransitionData
float Length;
};
class AnimGraphBox : public VisjectGraphBox
{
public:
AnimGraphBox()
{
}
AnimGraphBox(AnimGraphNode* parent, byte id, const VariantType::Types type)
: VisjectGraphBox(parent, id, type)
{
}
AnimGraphBox(AnimGraphNode* parent, byte id, const VariantType& type)
: VisjectGraphBox(parent, id, type)
{
}
};
typedef VisjectGraphBox AnimGraphBox;
class AnimGraphNode : public VisjectGraphNode<AnimGraphBox>
{
public:
struct MultiBlend1DData
{
/// <summary>
/// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
/// </summary>
// Amount of blend points.
ANIM_GRAPH_MULTI_BLEND_INDEX Count;
// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
float Length;
/// <summary>
/// The indices of the animations to blend. Sorted from the lowest X to the highest X. Contains only valid used animations. Unused items are using index ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS which is invalid.
/// </summary>
byte IndicesSorted[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS];
// The indices of the animations to blend. Sorted from the lowest X to the highest X. Contains only valid used animations. Unused items are using index ANIM_GRAPH_MULTI_BLEND_INVALID which is invalid.
ANIM_GRAPH_MULTI_BLEND_INDEX* IndicesSorted;
};
struct MultiBlend2DData
{
/// <summary>
/// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
/// </summary>
// Amount of blend points.
ANIM_GRAPH_MULTI_BLEND_INDEX Count;
// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback.
float Length;
/// <summary>
/// Cached triangles vertices (vertex 0). Contains list of indices for triangles to use for blending.
/// </summary>
byte TrianglesP0[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
/// <summary>
/// Cached triangles vertices (vertex 1). Contains list of indices for triangles to use for blending.
/// </summary>
byte TrianglesP1[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
/// <summary>
/// Cached triangles vertices (vertex 2). Contains list of indices for triangles to use for blending.
/// </summary>
byte TrianglesP2[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS];
// Amount of triangles.
int32 TrianglesCount;
// Cached triangles vertices (3 bytes per triangle). Contains list of indices for triangles to use for blending.
ANIM_GRAPH_MULTI_BLEND_INDEX* Triangles;
};
struct StateMachineData
@@ -493,15 +463,11 @@ public:
struct StateBaseData
{
/// <summary>
/// The invalid transition valid used in Transitions to indicate invalid transition linkage.
/// </summary>
// The invalid transition valid used in Transitions to indicate invalid transition linkage.
const static uint16 InvalidTransitionIndex = MAX_uint16;
/// <summary>
/// The outgoing transitions from this state to the other states. Each array item contains index of the transition data from the state node graph transitions cache. Value InvalidTransitionIndex is used for last transition to indicate the transitions amount.
/// </summary>
uint16 Transitions[ANIM_GRAPH_MAX_STATE_TRANSITIONS];
// The outgoing transitions from this state to the other states. Each array item contains index of the transition data from the state node graph transitions cache. Value InvalidTransitionIndex is used for last transition to indicate the transitions amount.
uint16* Transitions;
};
struct StateData : StateBaseData
@@ -597,12 +563,14 @@ public:
{
}
~AnimGraphNode();
public:
/// <summary>
/// Gets the per-node node transformations cache (cached).
/// </summary>
/// <param name="executor">The Graph execution context.</param>
/// <returns>The modes data.</returns>
/// <returns>Nodes data.</returns>
AnimGraphImpulse* GetNodes(AnimGraphExecutor* executor);
};
@@ -799,7 +767,7 @@ struct AnimGraphContext
bool StackOverFlow;
Array<VisjectExecutor::Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> CallStack;
Array<VisjectExecutor::Graph*, FixedAllocation<32>> GraphStack;
Array<uint32, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK> > NodePath;
Array<uint32, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> NodePath;
Dictionary<VisjectExecutor::Node*, VisjectExecutor::Graph*> Functions;
ChunkedArray<AnimGraphImpulse, 256> PoseCache;
int32 PoseCacheSize;
@@ -892,7 +860,7 @@ private:
int32 GetRootNodeIndex(Animation* anim);
void ProcessAnimEvents(AnimGraphNode* node, bool loop, float length, float animPos, float animPrevPos, Animation* anim, float speed);
void ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight = 1.0f, ProcessAnimationMode mode = ProcessAnimationMode::Override);
void ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight = 1.0f, ProcessAnimationMode mode = ProcessAnimationMode::Override, BitArray<InlinedAllocation<8>>* usedNodes = nullptr);
Variant SampleAnimation(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* anim, float speed);
Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, float speedA, float speedB, float alpha);
Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, Animation* animC, float speedA, float speedB, float speedC, float alphaA, float alphaB, float alphaC);
@@ -25,7 +25,7 @@ namespace
{
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
{
nodes->Nodes[i].Orientation.Normalize();
nodes->Nodes.Get()[i].Orientation.Normalize();
}
if (rootMotionMode != RootMotionExtraction::NoExtraction)
{
@@ -222,7 +222,7 @@ FORCE_INLINE void GetAnimSamplePos(bool loop, float length, float startTimePos,
prevPos = GetAnimPos(prevTimePos, startTimePos, loop, length);
}
void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight, ProcessAnimationMode mode)
void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* node, bool loop, float length, float pos, float prevPos, Animation* anim, float speed, float weight, ProcessAnimationMode mode, BitArray<InlinedAllocation<8>>* usedNodes)
{
PROFILE_CPU_ASSET(anim);
@@ -240,9 +240,17 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
}
// Evaluate nested animations
bool hasNested = false;
BitArray<InlinedAllocation<8>> usedNodesThis;
if (anim->NestedAnims.Count() != 0)
{
if (usedNodes == nullptr)
{
// Per-channel bit to indicate which channels were used by nested
usedNodesThis.Resize(nodes->Nodes.Count());
usedNodesThis.SetAll(false);
usedNodes = &usedNodesThis;
}
for (auto& e : anim->NestedAnims)
{
const auto& nestedAnim = e.Second;
@@ -262,8 +270,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
nestedAnimPrevPos = nestedAnimPrevPos * frameRateMatchScale;
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode);
hasNested = true;
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode, usedNodes);
}
}
}
@@ -280,11 +287,11 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
SkinnedModel::SkeletonMapping sourceMapping;
if (retarget)
sourceMapping = _graph.BaseModel->GetSkeletonMapping(mapping.SourceSkeleton);
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
for (int32 nodeIndex = 0; nodeIndex < nodes->Nodes.Count(); nodeIndex++)
{
const int32 nodeToChannel = mapping.NodesMapping[i];
Transform& dstNode = nodes->Nodes[i];
Transform srcNode = emptyNodes->Nodes[i];
const int32 nodeToChannel = mapping.NodesMapping[nodeIndex];
Transform& dstNode = nodes->Nodes[nodeIndex];
Transform srcNode = emptyNodes->Nodes[nodeIndex];
if (nodeToChannel != -1)
{
// Calculate the animated node transformation
@@ -293,8 +300,17 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
// Optionally retarget animation into the skeleton used by the Anim Graph
if (retarget)
{
RetargetSkeletonNode(mapping.SourceSkeleton->Skeleton, mapping.TargetSkeleton->Skeleton, sourceMapping, srcNode, i);
RetargetSkeletonNode(mapping.SourceSkeleton->Skeleton, mapping.TargetSkeleton->Skeleton, sourceMapping, srcNode, nodeIndex);
}
// Mark node as used
if (usedNodes)
usedNodes->Set(nodeIndex, true);
}
else if (usedNodes && (usedNodes != &usedNodesThis || usedNodes->Get(nodeIndex)))
{
// Skip for nested animations so other one or top-level anim will update remaining nodes
continue;
}
// Blend node
@@ -316,7 +332,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
dstNode.Scale = srcNode.Scale * weight;
dstNode.Orientation = srcNode.Orientation * weight;
}
else if (!hasNested)
else
{
dstNode = srcNode;
}
@@ -570,7 +586,7 @@ AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphCon
AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, const AnimGraphNode::StateBaseData& stateData, AnimGraphNode* state, AnimGraphNode* ignoreState)
{
int32 transitionIndex = 0;
while (transitionIndex < ANIM_GRAPH_MAX_STATE_TRANSITIONS && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex)
while (stateData.Transitions && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex)
{
const uint16 idx = stateData.Transitions[transitionIndex];
ASSERT(idx < stateMachineData.Graph->StateTransitions.Count());
@@ -658,19 +674,20 @@ void ComputeMultiBlendLength(float& length, AnimGraphNode* node)
// TODO: lock graph or graph asset here? make it thread safe
length = 0.0f;
for (int32 i = 0; i < ARRAY_COUNT(node->Assets); i++)
for (int32 i = 0; i < node->Assets.Count(); i++)
{
if (node->Assets[i])
auto& asset = node->Assets[i];
if (asset)
{
// TODO: maybe don't update if not all anims are loaded? just skip the node with the bind pose?
if (node->Assets[i]->WaitForLoaded())
if (asset->WaitForLoaded())
{
node->Assets[i] = nullptr;
asset = nullptr;
LOG(Warning, "Failed to load one of the animations.");
}
else
{
const auto anim = node->Assets[i].As<Animation>();
const auto anim = asset.As<Animation>();
const auto aData = node->Values[4 + i * 2].AsFloat4();
length = Math::Max(length, anim->GetLength() * Math::Abs(aData.W));
}
@@ -1177,14 +1194,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
{
const float alpha = Math::Saturate((float)tryGetValue(node->GetBox(3), node->Values[0]));
auto mask = node->Assets[0].As<SkeletonMask>();
auto maskAssetBox = node->GetBox(4); // 4 is the id of skeleton mask parameter node.
// Check if have some mask asset connected with the mask node
if (maskAssetBox->HasConnection())
// Use the mask connected with this node instead of default mask asset
auto maskAssetBox = node->TryGetBox(4);
if (maskAssetBox && maskAssetBox->HasConnection())
{
const Value assetBoxValue = tryGetValue(maskAssetBox, Value::Null);
// Use the mask connected with this node instead of default mask asset
if (assetBoxValue != Value::Null)
mask = (SkeletonMask*)assetBoxValue.AsAsset;
}
@@ -1255,13 +1270,20 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
auto& data = node->Data.MultiBlend1D;
// Check if not valid animation binded
if (data.IndicesSorted[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
if (data.Count == 0)
break;
// Get axis X
float x = (float)tryGetValue(node->GetBox(4), Value::Zero);
x = Math::Clamp(x, range.X, range.Y);
// Add to trace
if (context.Data->EnableTracing)
{
auto& trace = context.AddTraceEvent(node);
trace.Value = x;
}
// Check if need to evaluate multi blend length
if (data.Length < 0)
ComputeMultiBlendLength(data.Length, node);
@@ -1279,7 +1301,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
ANIM_GRAPH_PROFILE_EVENT("Multi Blend 1D");
// Find 2 animations to blend (line)
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS - 1; i++)
for (int32 i = 0; i < data.Count - 1; i++)
{
const auto a = data.IndicesSorted[i];
const auto b = data.IndicesSorted[i + 1];
@@ -1289,14 +1311,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
auto aData = node->Values[4 + a * 2].AsFloat4();
// Check single A case or the last valid animation
if (x <= aData.X + ANIM_GRAPH_BLEND_THRESHOLD || b == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
if (x <= aData.X + ANIM_GRAPH_BLEND_THRESHOLD || b == ANIM_GRAPH_MULTI_BLEND_INVALID)
{
value = SampleAnimation(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, aData.W);
break;
}
// Get B animation data
ASSERT(b != ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS);
ASSERT(b != ANIM_GRAPH_MULTI_BLEND_INVALID);
const auto bAnim = node->Assets[b].As<Animation>();
auto bData = node->Values[4 + b * 2].AsFloat4();
@@ -1344,7 +1366,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
auto& data = node->Data.MultiBlend2D;
// Check if not valid animation binded
if (data.TrianglesP0[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
if (data.TrianglesCount == 0)
break;
// Get axis X
@@ -1355,6 +1377,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
float y = (float)tryGetValue(node->GetBox(5), Value::Zero);
y = Math::Clamp(y, range.Z, range.W);
// Add to trace
if (context.Data->EnableTracing)
{
auto& trace = context.AddTraceEvent(node);
const Half2 packed(x, y); // Pack xy into 32-bits
*(uint32*)&trace.Value = *(uint32*)&packed;
}
// Check if need to evaluate multi blend length
if (data.Length < 0)
ComputeMultiBlendLength(data.Length, node);
@@ -1377,20 +1407,20 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
Float2 bestPoint;
float bestWeight = 0.0f;
byte bestAnims[2];
for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS && data.TrianglesP0[i] != ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++)
for (int32 i = 0, t = 0; i < data.TrianglesCount; i++)
{
// Get A animation data
const auto a = data.TrianglesP0[i];
const auto a = data.Triangles[t++];
const auto aAnim = node->Assets[a].As<Animation>();
const auto aData = node->Values[4 + a * 2].AsFloat4();
// Get B animation data
const auto b = data.TrianglesP1[i];
const auto b = data.Triangles[t++];
const auto bAnim = node->Assets[b].As<Animation>();
const auto bData = node->Values[4 + b * 2].AsFloat4();
// Get C animation data
const auto c = data.TrianglesP2[i];
const auto c = data.Triangles[t++];
const auto cAnim = node->Assets[c].As<Animation>();
const auto cData = node->Values[4 + c * 2].AsFloat4();

Some files were not shown because too many files have changed in this diff Show More