243 Commits

Author SHA1 Message Date
c1f6bcb83f some linux work from summer
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-10-12 18:06:23 +03:00
eca4f6d9bc Merge remote-tracking branch 'origin/1.11' into work
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-07-05 14:19:52 +03:00
802074fca3 Update package requirements for Wayland 2025-07-05 12:55:25 +03:00
0bb13e1d91 Add overloads for physics queries with user-provided buffers 2025-07-05 12:36:11 +03:00
db47531a6d Support marshalling Arrays as dynamic arrays in scripting API 2025-07-05 12:36:11 +03:00
Wojtek Figat
48c6339ebb Fix memory leak on material instances when updating layout of Text Render 2025-07-04 12:21:25 +02:00
Wojtek Figat
2dd34b288c Merge remote-tracking branch 'origin/master' into 1.11 2025-07-04 12:05:19 +02:00
Wojtek Figat
0bc595f16f Disable terarin normals smoothening 2025-07-04 12:04:36 +02:00
Wojtek Figat
7abed93972 Optimize terrain heightmap decoding to use shared code 2025-07-04 11:31:27 +02:00
51feaa0730 Release mouse relative mode when focus changes during mouse tracking
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-07-03 23:21:42 +03:00
8e69aa8bd6 Fix error while closing Editor
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-07-03 22:57:07 +03:00
90b2fded48 Merge remote-tracking branch 'origin/master' into sdl_platform
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
# Conflicts:
#	Source/Editor/Windows/SceneTreeWindow.cs
2025-07-03 22:05:29 +03:00
9125ffeb9e Update third-party label in About dialog 2025-07-03 22:02:37 +03:00
28980e5fbf Cleanup 2025-07-03 22:02:36 +03:00
aab0d772a4 Implement client-side window decorations for Editor windows 2025-07-03 22:02:36 +03:00
Wojtek Figat
4b10d7057a Fix crash when using material instance that has more parameters that base due to material error 2025-07-03 20:33:14 +02:00
Wojtek Figat
85aed8c4d7 Fix using material VS to PS node directly within material input 2025-07-03 20:32:41 +02:00
Wojtek Figat
da08be42b4 Fix deadlock in Debug builds on object dtor 2025-07-03 20:32:12 +02:00
Wojtek Figat
056fea9a8c Fix forward shading compilation when using fog inside unlit material 2025-07-03 14:48:38 +02:00
Wojtek Figat
bf345f13ce Fix reflection probes capture seams on cube face edges due to volumetric fog
#3252
2025-07-03 13:54:22 +02:00
Wojtek Figat
a138c6b062 Optimize environment probes filtering shader 2025-07-03 11:45:12 +02:00
Wojtek Figat
33e58c12cb Optimize ProbesRenderer to use time-slicing for cubemap faces rendering and filtering 2025-07-03 11:43:56 +02:00
Wojtek Figat
094a6562b8 Refactor ProbesRenderer 2025-07-03 10:18:51 +02:00
Wojtek Figat
7f783bb7da Fix crash when importing mesh with incorrect triangulation 2025-07-02 17:23:04 +02:00
Wojtek Figat
b08f218f30 Merge branch 'Zode-tageditor-quickexpand' 2025-07-01 16:47:07 +02:00
Wojtek Figat
c6640be04a Merge branch 'tageditor-quickexpand' of https://github.com/Zode/FlaxEngine into Zode-tageditor-quickexpand 2025-07-01 16:47:04 +02:00
Wojtek Figat
eff3c50d9c Merge branch 'xxSeys1-ContentSearchMouseOverFocusFix' 2025-07-01 16:34:42 +02:00
Wojtek Figat
3fa099ad06 Merge branch 'ContentSearchMouseOverFocusFix' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-ContentSearchMouseOverFocusFix 2025-07-01 16:27:08 +02:00
Wojtek Figat
5e47a861f3 Merge branch 'xxSeys1-VisjectConnectionCmPolish' 2025-07-01 16:06:22 +02:00
Wojtek Figat
37b4b25ead Merge branch 'VisjectConnectionCmPolish' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-VisjectConnectionCmPolish 2025-07-01 16:06:13 +02:00
Wojtek Figat
edfd145226 Merge branch 'xxSeys1-FixCloseButtonAfterIBrokeIt' 2025-07-01 15:58:46 +02:00
Wojtek Figat
763ede4bd9 Merge branch 'FixCloseButtonAfterIBrokeIt' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-FixCloseButtonAfterIBrokeIt 2025-07-01 15:58:36 +02:00
Wojtek Figat
284da341e1 Merge branch 'xxSeys1-ItemsListExpandOnNavigate' 2025-07-01 15:56:53 +02:00
Wojtek Figat
53ed2431b5 Merge branch 'ItemsListExpandOnNavigate' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-ItemsListExpandOnNavigate 2025-07-01 15:48:24 +02:00
Wojtek Figat
b4ab39b3bf Merge branch 'Tryibion-keep-scroll-scene-play' 2025-07-01 15:34:27 +02:00
Wojtek Figat
de61e3efb5 Merge branch 'keep-scroll-scene-play' of https://github.com/Tryibion/FlaxEngine into Tryibion-keep-scroll-scene-play 2025-07-01 15:34:07 +02:00
Wojtek Figat
29fb36fcd6 Merge branch 'Tryibion-fix-log-clear-play' 2025-07-01 10:52:39 +02:00
Wojtek Figat
78f0ff75f2 Move debug log clearing to handle pending entries too in publci api 2025-07-01 10:52:14 +02:00
Wojtek Figat
6efd456eaf Merge branch 'fix-log-clear-play' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-log-clear-play 2025-07-01 10:50:58 +02:00
Wojtek Figat
e0c96c0cde Merge branch 'Tryibion-fix-tooltip-scene' 2025-07-01 10:48:34 +02:00
Wojtek Figat
ada269568a Merge branch 'fix-tooltip-scene' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-tooltip-scene 2025-07-01 10:48:26 +02:00
Wojtek Figat
dd256ad518 Fix typo 2025-07-01 10:37:39 +02:00
Wojtek Figat
12c8bb4f31 Fix scene lightmaps removal clearing actors linkage to lightmaps 2025-07-01 08:27:29 +02:00
Wojtek Figat
4e4d380267 Fix fog to draw Fog Cutoff Distance via a plane, not sphere test
Add support for negative Fog Cutoff Distance on fog to draw it in front of the camera Far Plane, no matter the setup.
Fix hot-reloading Fog shader in Editor.
2025-06-29 20:02:54 +02:00
Wojtek Figat
448eb48c23 Fix fog to draw Fog Cutoff Distance via a plane, not sphere test
Add support for negative Fog Cutoff Distance on fog to draw it in front of the camera Far Plane, no matter the setup.
Fix hot-reloading Fog shader in Editor.
2025-06-29 20:02:24 +02:00
Wojtek Figat
78d519cb9a Fix ConcurrentSystemLocker to have exclusive lock as an option 2025-06-29 19:16:41 +02:00
Wojtek Figat
43d11264f8 Fix asset references to use separate lightweight locking instead of full asset mutex 2025-06-29 19:16:23 +02:00
Wojtek Figat
f126a83b79 Fix graphical issues when batching materials that use Forward Shading for instancing 2025-06-29 13:52:29 +02:00
Wojtek Figat
bdd7bae459 Add new Custom Lit shading model for custom lighting in materials (eg. Cel Shading) 2025-06-29 13:51:59 +02:00
Wojtek Figat
3dc7546dd4 Fix crash when constant buffer is unused by shader but still exists 2025-06-27 19:06:25 +02:00
Wojtek Figat
185151b025 Minor fixes 2025-06-27 18:52:25 +02:00
Wojtek Figat
8cdec15fa6 Fix GlobalSignDistanceFieldCustomBuffer to be thread-safe (scene rendering events are not guarded via mutex anymore) 2025-06-27 15:41:48 +02:00
Wojtek Figat
1b40775d62 Fix deadloop in HtmlParser when parsing text with incorrect tags 2025-06-27 11:56:09 +02:00
Wojtek Figat
45e82d21f4 Fix ConcurrentSystemLocker to guard for a single writer at once 2025-06-26 19:51:06 +02:00
Wojtek Figat
5c37584eca Minor adjustment for alignment of perf-critical variables in rendering 2025-06-26 19:50:42 +02:00
Wojtek Figat
674fda7375 Add resizing to Custom Code nodes in Materials 2025-06-26 19:50:04 +02:00
6296e1a9eb Fix linker errors 2025-06-26 15:32:38 +03:00
1586bb0702 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-26 15:31:59 +03:00
xxSeys1
bf10d0949e add a bit of spacing to the left edge of the popup to reduce colors bleeding into the background 2025-06-26 12:47:31 +02:00
Wojtek Figat
d1c43ec1fe Merge remote-tracking branch 'origin/master' into 1.11 2025-06-25 10:48:11 +02:00
edeaf6af09 Fix compilation without SDL 2025-06-24 22:57:49 +03:00
Wojtek Figat
dbd8297612 Add SortOrder to Environment Probe 2025-06-24 19:25:18 +02:00
Wojtek Figat
8d62a13cbc Add LayoutElementsContainer.Image with IBrush 2025-06-24 18:32:20 +02:00
951edd95db Cleanup 2025-06-24 16:47:45 +03:00
9951211596 Fix mono glib.h wrapper compilation error 2025-06-24 16:47:20 +03:00
6d337464f7 Support XDG Desktop Portal as color picker provider
Wayland and XWayland fallback implementation uses XDP in
order to query picked color from desktop.
2025-06-24 16:41:32 +03:00
Wojtek Figat
5a05038a9b Add new UIBrush that uses UIBrushAsset json resource with a brush data 2025-06-24 13:08:25 +02:00
xxSeys1
109d4423bb add accent color strip to search item icons 2025-06-23 20:14:18 +02:00
d0b552d74a Use logo as an icon in Linux windows 2025-06-23 18:58:02 +03:00
xxSeys1
6c1e380a3e fix search item hover auto focusing item
Change what happens when search items get focused to prevent focus being taken away from search box.
Also adds a highlight to mouse hovered search item.
2025-06-23 17:24:53 +02:00
83512822b1 Support clipboard actions in Content Browser and Scene tree on Wayland
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-06-23 17:50:02 +03:00
ccb78103ec Support clipboard actions in Content Browser and Scene tree on X11 2025-06-23 17:07:10 +03:00
6e79ffea34 Fix clipboard not working on X11 2025-06-23 17:07:10 +03:00
4f03d37a17 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-23 12:03:41 +03:00
Wojtek Figat
c57a1a7205 Fix particle emitters to wait for used textures to be loaded and streamed in for proper usage in a graph 2025-06-23 09:31:58 +02:00
Wojtek Figat
867ae2ceaa Add Texture Size node to particles 2025-06-23 09:31:15 +02:00
Wojtek Figat
ef5d45874a Fix compilation regression 2025-06-22 12:12:42 +02:00
Chandler Cox
169db79b2a Fix issue with tooltip not updating on scene reload. 2025-06-20 20:12:43 -05:00
Chandler Cox
9e4c1da032 Clear entries and pending entries before play for debug log. 2025-06-20 19:42:30 -05:00
Wojtek Figat
d7df403e5e Optimize ContainerControl.DisposeChildren 2025-06-20 09:05:41 +02:00
Wojtek Figat
d3a50cdacb Optimize Actor::DestroyChildren 2025-06-20 09:05:25 +02:00
Chandler Cox
3cb74d48ca Convert using event to using window method overrides. 2025-06-19 20:01:27 -05:00
Chandler Cox
8633ed2124 Merge branch 'master' into keep-scroll-scene-play 2025-06-19 19:58:04 -05:00
Wojtek Figat
2e10d776e9 Optimize updating actor rendering entry with better thread locking that support async writes on actor update 2025-06-19 14:04:06 +02:00
Wojtek Figat
4ac870f701 Optimize physics transformation updates propagation in async via Job System 2025-06-19 13:57:50 +02:00
Wojtek Figat
6144f6c74e Optimize physics simulation with higher limit of 8 threads 2025-06-19 09:50:07 +02:00
Wojtek Figat
edb6884942 Optimize PhysX work dispatcher to be shared by all scenes 2025-06-19 08:24:26 +02:00
Wojtek Figat
62e329ac6e Add more memory tags for Navigation 2025-06-18 23:00:43 +02:00
Wojtek Figat
68dce7e4dd Merge remote-tracking branch 'origin/master' into 1.11 2025-06-18 22:46:10 +02:00
Wojtek Figat
6479a3d3c6 Fix log spam in task when waiting for task to end 2025-06-18 22:41:42 +02:00
Wojtek Figat
e41c48f9e5 Optimize NavMesh builder when updating a large number of tiles 2025-06-18 22:41:26 +02:00
Wojtek Figat
9cfcbfa9d2 Fix memory leak on recast objects when building NavMesh tile 2025-06-17 23:28:15 +02:00
Wojtek Figat
bcd2b1f0f2 Fix Array::RemoveAt to move element and avoid data copies 2025-06-17 23:27:26 +02:00
6fb8419b3c Disable window flashing on Wayland to fix focus issues 2025-06-17 14:23:20 +03:00
a4272d6ca9 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-17 14:19:46 +03:00
Wojtek Figat
eb6010cba7 Fix BitArray again 2025-06-16 23:20:20 +02:00
Wojtek Figat
bd2add7edd Tweak memory command tip 2025-06-16 23:15:58 +02:00
Wojtek Figat
986693757c Merge remote-tracking branch 'origin/master' into 1.11 2025-06-16 22:46:17 +02:00
Wojtek Figat
5a23060e05 Add GPUTexture.UploadData for changing texture contents via TextureData container 2025-06-16 22:35:50 +02:00
Wojtek Figat
b92c18cf25 Fix missing/incorrect toolchain exception to log only once 2025-06-16 19:10:35 +02:00
Wojtek Figat
6a82eb114d Fix BitArray bit indexing 2025-06-16 19:05:44 +02:00
Wojtek Figat
97b37b3ce4 Add PRAGMA_DISABLE_OPTIMIZATION/PRAGMA_ENABLE_OPTIMIZATION 2025-06-16 18:00:26 +02:00
xxSeys1
bbe54969b0 fix close visject surface was_ flags to properly reset to false 2025-06-16 16:51:30 +02:00
4654117d5e Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-16 17:09:22 +03:00
xxSeys1
8164ce924f fix unfolding to actually work 2025-06-16 15:30:51 +02:00
Wojtek Figat
22911bbc45 Merge branch 'xxSeys1-VisjectCloseButtonFix' 2025-06-16 15:02:37 +02:00
Wojtek Figat
cae2720585 Merge branch 'VisjectCloseButtonFix' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-VisjectCloseButtonFix 2025-06-16 15:02:32 +02:00
Wojtek Figat
90b6293bc2 Add OnPlayEnding to editor modules and windows
#3514
2025-06-16 14:56:35 +02:00
xxSeys1
d110237423 fix GetVisibleItems() to only get visible items 2025-06-16 14:51:29 +02:00
Wojtek Figat
117f890612 Merge branch 'Zode-scenetree-scroll' 2025-06-16 14:50:14 +02:00
Wojtek Figat
a6a2fd2c66 Format code
#3526
2025-06-16 14:48:18 +02:00
Wojtek Figat
a05ca12770 Merge branch 'scenetree-scroll' of https://github.com/Zode/FlaxEngine into Zode-scenetree-scroll 2025-06-16 14:44:54 +02:00
Wojtek Figat
abe7785e94 Merge branch 'xxSeys1-PrefabEditorScrollToSelectionFix' 2025-06-16 14:43:40 +02:00
Wojtek Figat
2cedac8d96 Merge branch 'PrefabEditorScrollToSelectionFix' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-PrefabEditorScrollToSelectionFix 2025-06-16 14:43:34 +02:00
Wojtek Figat
ea854a0f7b Fix potential Grid shader accuracy issues
#3229
2025-06-16 14:41:43 +02:00
Wojtek Figat
d0817809ff Merge branch 'xxSeys1-ContentFinderImprovements' 2025-06-16 14:34:25 +02:00
Wojtek Figat
95f2216f97 Merge branch 'ContentFinderImprovements' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-ContentFinderImprovements 2025-06-16 14:32:31 +02:00
Wojtek Figat
7e5edf1b33 Merge branch 'xxSeys1-ClearSearchOnAssetReferenceNavigate' 2025-06-16 14:30:58 +02:00
Wojtek Figat
c649a4f4b2 Merge branch 'ClearSearchOnAssetReferenceNavigate' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-ClearSearchOnAssetReferenceNavigate 2025-06-16 14:30:53 +02:00
xxSeys1
cd7233d559 allow the user to unfold folded categories when scrolling with keyboard 2025-06-16 14:10:44 +02:00
6ff260d052 Merge remote-tracking branch 'origin/master' into sdl_platform 2025-06-16 14:09:29 +03:00
88d2b72822 Update SDL to 3.3.x
Fixes popup focus issues on Linux
2025-06-16 14:09:04 +03:00
Wojtek Figat
3c74b315a8 Merge branch 'Zode-console-hotkey' 2025-06-15 23:47:29 +02:00
Wojtek Figat
f841575378 Merge branch 'console-hotkey' of https://github.com/Zode/FlaxEngine into Zode-console-hotkey 2025-06-15 23:25:09 +02:00
Wojtek Figat
d90377ed62 Merge branch 'Zode-import-export-warnings' 2025-06-15 23:23:22 +02:00
Wojtek Figat
c8d622bda9 Merge branch 'import-export-warnings' of https://github.com/Zode/FlaxEngine into Zode-import-export-warnings 2025-06-15 23:23:18 +02:00
Wojtek Figat
fbf2d8f567 Merge branch 'xxSeys1-ItemListImprovedKeyboardNav' 2025-06-15 23:06:26 +02:00
Wojtek Figat
42687a792c Merge branch 'ItemListImprovedKeyboardNav' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-ItemListImprovedKeyboardNav 2025-06-15 22:59:20 +02:00
Wojtek Figat
5c43d35146 Merge branch 'Zode-node-alignment' 2025-06-15 22:32:29 +02:00
Wojtek Figat
c1e1dee975 Merge branch 'node-alignment' of https://github.com/Zode/FlaxEngine into Zode-node-alignment 2025-06-15 22:32:24 +02:00
Wojtek Figat
742d74a2d0 Merge branch 'xxSeys1-ModelSliders' 2025-06-15 21:43:04 +02:00
Wojtek Figat
614ffa70a0 Merge branch 'ModelSliders' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-ModelSliders 2025-06-15 21:43:00 +02:00
Wojtek Figat
4b4331fd70 Merge branch 'Zode-visject-better-selection' 2025-06-15 21:28:42 +02:00
Wojtek Figat
e221f7204b Merge branch 'visject-better-selection' of https://github.com/Zode/FlaxEngine into Zode-visject-better-selection 2025-06-15 21:22:27 +02:00
Wojtek Figat
18fa8e1c47 Merge branch 'Zode-particles-sourcecode' 2025-06-15 20:48:22 +02:00
Wojtek Figat
2f02ec52ed Cleanup code
#3546
2025-06-15 20:48:19 +02:00
Wojtek Figat
9464f97e16 Merge branch 'particles-sourcecode' of https://github.com/Zode/FlaxEngine into Zode-particles-sourcecode 2025-06-15 20:45:18 +02:00
Wojtek Figat
54467a4a5e Merge branch 'Zode-layers-matrix-improvements' 2025-06-15 20:41:23 +02:00
Wojtek Figat
3a9e175043 Merge branch 'layers-matrix-improvements' of https://github.com/Zode/FlaxEngine into Zode-layers-matrix-improvements 2025-06-15 20:40:22 +02:00
Wojtek Figat
9dff048cb5 Merge branch 'Tryibion-main-menu-get-add' 2025-06-15 20:36:37 +02:00
Wojtek Figat
f376fd50a9 Merge branch 'main-menu-get-add' of https://github.com/Tryibion/FlaxEngine into Tryibion-main-menu-get-add 2025-06-15 20:36:32 +02:00
Wojtek Figat
e27cf04f91 Merge branch 'Zode-debug-quicktoggle' 2025-06-15 20:32:00 +02:00
Wojtek Figat
648504ceb1 Format code
#3544
2025-06-15 20:31:04 +02:00
xxSeys1
e2f741cab9 adjust model window and animated model slider speeds 2025-06-12 18:45:02 +02:00
Wojtek Figat
766091045b Improve version parsing to share code 2025-06-12 18:21:12 +02:00
xxSeys1
47e1547d29 improve behaviour of visject node cm menu 2025-06-12 18:12:06 +02:00
Wojtek Figat
e2d9452994 Add unified min Clang version 13 for Linux 2025-06-12 18:05:01 +02:00
Wojtek Figat
eadb4411ff Fix crash in GPU Memory profiler if resource went null 2025-06-12 17:35:02 +02:00
Wojtek Figat
bdc87c7bc6 Update min supported version of macOS to 13 and iOS to 15 2025-06-12 17:26:39 +02:00
Wojtek Figat
7606c9ac12 Update minimum CPU arch requirement on Windows to AVX2 with SSE4.2
94.48% support on PC according to Steam Hardware & Software Survey: May 2025 (https://store.steampowered.com/hwsurvey/)
2025-06-12 17:03:19 +02:00
Wojtek Figat
4240646ec7 Update minimum Windows version to 10 (to match .NET 8) 2025-06-12 08:31:32 +02:00
xxSeys1
214ec9f2b1 fix node delete button not checking if the user is performing certain actions on the surface before deleting node 2025-06-12 00:49:39 +02:00
Wojtek Figat
0fa53f860a Add UseLogInRelease to engine config to disable logging in Release builds 2025-06-11 23:35:03 +02:00
Zode
27ac755bbe Make particle emitter editor window source code button disable itself is no source code is available 2025-06-11 23:15:11 +03:00
Wojtek Figat
8ec138399a Add higher level streaming time budget in frame based on idle time 2025-06-11 18:40:35 +02:00
Wojtek Figat
5b6859a66f Add time slicing to Deserialization stage of async scenes loading to avoid hitching
#3261
2025-06-11 18:40:06 +02:00
Zode
bed1f6e9cc Make tag editor expand all if holding shift while clicking the ... button 2025-06-11 19:06:23 +03:00
Zode
00055ef663 Make tool strip buttons more responsive by also reaction to double left clicks 2025-06-11 17:47:27 +03:00
Zode
aa59a6faf7 Extract function to lessen repeat code for debug group buttons, add shift functionality to quick toggle others. 2025-06-11 17:46:37 +03:00
Wojtek Figat
e9835766bc Add red color to Tracy profiler zones that cause CPU waiting to improve profiling 2025-06-11 14:56:43 +02:00
Wojtek Figat
d6eb647d59 Optimize async scene loading to run in separate stages with time-slicing 2025-06-11 14:33:47 +02:00
Wojtek Figat
b50f3fcb64 Refactor level actions to support time budget and time slicing 2025-06-11 00:01:46 +02:00
Wojtek Figat
d6b4992991 Optimize actors registration in SceneRendering to track free items 2025-06-10 20:08:20 +02:00
Chandler Cox
fc561b5ec5 Merge pull request #2 from xxSeys1/noScrollOnScriptsReload
Dont scroll to node after scripts compilation
2025-06-10 10:03:06 -05:00
xxSeys1
dacc0068e0 dont scroll to node after scripts compilation 2025-06-10 16:49:30 +02:00
Zode
e7d2025010 Merge less flicker pr, and snatch the badge
Co-authored-by: Zode <jonzqzode@gmail.com>
Co-authored-by: Saas <kaktusreiniger@gmail.com>
2025-06-10 16:45:02 +03:00
xxSeys1
b418ab5275 reduce flickering in highlights and boxes 2025-06-10 14:58:31 +02:00
Zode
c1e782bb32 Add hotkey to quick focus debug console input 2025-06-09 23:34:02 +03:00
Wojtek Figat
cfd2f42b0c Optimize managed memory allocations in Editor profiler 2025-06-09 22:06:49 +02:00
Chandler Cox
e1a2a36978 Add GetOrAddButton utility for MainMenu. 2025-06-09 15:02:43 -05:00
xxSeys1
793bc33b2f fix right clicking on item reset search and scroll 2025-06-09 18:40:33 +02:00
xxSeys1
2109a2d261 update content finder popup to wrap on keyboard navigation
- Fixes auto scroll
- Make it wrap
- Add pressing backspace to focus search bar and select all text
2025-06-09 18:40:12 +02:00
Wojtek Figat
89c7f4b0a3 Fix ManagedDictionary cache to be cleared on hot-reload 2025-06-09 17:19:36 +02:00
Wojtek Figat
d7ff9fdade Optimize editor profiler native allocations when capturing data 2025-06-09 15:23:31 +02:00
Zode
9e50a39ebf Add define guards so that the messagebox only appears in editor builds 2025-06-09 16:19:39 +03:00
Zode
7fc564a0ac Pop error box on lightmap UV generation also 2025-06-09 15:07:52 +03:00
Zode
8dfb564fb3 Add messagebox to windows import too 2025-06-09 15:04:34 +03:00
Zode
f8daff273a Add import & export warning messageboxes. 2025-06-09 14:56:54 +03:00
xxSeys1
112e728465 fix scroll to node on selection in prefab editor 2025-06-09 13:56:41 +02:00
Wojtek Figat
057ec9d41e Anothher fix 2025-06-09 10:48:02 +02:00
Wojtek Figat
7fa4efcac5 Fix compilation in Release 2025-06-09 10:17:51 +02:00
Wojtek Figat
6547e7ee9c Fix compilation with Clang 2025-06-08 23:58:33 +02:00
Wojtek Figat
907c593671 Fix typos in doc comments 2025-06-08 19:47:09 +02:00
Zode
9c9d560ce5 Add hotkeys to visject formatting 2025-06-08 03:32:51 +03:00
Zode
f1945552ab Add horizontal and vertical distribution 2025-06-08 02:42:28 +03:00
Wojtek Figat
65ab42158d Update engine version 2025-06-08 00:58:39 +02:00
Wojtek Figat
99841e2e8d Fix crash when using invalid node index in skinned mesh 2025-06-08 00:58:31 +02:00
Wojtek Figat
73c30d3d89 Optimize asset references to support direct registration to reduce Delegate memory allocations and overhead 2025-06-08 00:58:15 +02:00
Zode
ecd5559aad Clean up a bit code after moving it around. 2025-06-08 00:41:02 +03:00
Zode
1fa83639c2 Fix update order inconsistencies between machines by exposing an action for when defered layout happens. 2025-06-08 00:38:59 +03:00
Zode
568719b615 Fix scroll issue caused by tree's defered layout update 2025-06-07 23:21:41 +03:00
Zode
74000fa766 Expand tree if necessary, so the selected node will be shown 2025-06-07 22:53:07 +03:00
Zode
1d63067614 Add ability to do additive and subtractive selections in visject surfaces using ctrl and shift during selection. 2025-06-07 21:04:59 +03:00
xxSeys1
85fd1389db clear content panel search when selecting asset picker asset 2025-06-07 18:17:40 +02:00
xxSeys1
cce042045e wrap scrolling items with arrow keys and simplify scrolling logic 2025-06-07 17:34:01 +02:00
Zode
53e3cee196 Scroll selected to view when emptying out search box 2025-06-07 17:15:54 +03:00
Zode
6ee3b23261 Add highlighting to layers matrix editor to improve UX 2025-06-07 16:57:28 +03:00
Zode
1a77ba4552 Add node alignment formatting options to visject 2025-06-07 13:15:39 +03:00
Wojtek Figat
bffb175a9b Code fixes 2025-06-07 01:25:22 +02:00
Wojtek Figat
125a973ff2 Rename Prefetch to MemoryPrefetch 2025-06-06 22:55:14 +02:00
Wojtek Figat
462f75abd0 Optimize memory allocation when reading animated model pose by cloth 2025-06-06 22:41:48 +02:00
Wojtek Figat
d95cd2f0be Optimize memory alloc on Animated Model init 2025-06-06 22:41:29 +02:00
Wojtek Figat
091f76bbf2 Add more improvements to usability of memory profiler 2025-06-06 22:40:43 +02:00
Wojtek Figat
e8b60060ab Fix memory profiler thread-local storage to avoid dynamic mem alloc due to recursive call 2025-06-06 14:52:27 +02:00
Wojtek Figat
cd637e8a7a Add more memory profiling coverage 2025-06-06 14:38:22 +02:00
Wojtek Figat
9d8e75caa3 Fix various code to improve quality 2025-06-06 11:19:32 +02:00
Wojtek Figat
eee4e55cf0 Fix debug shapes change 2025-06-05 22:26:45 +02:00
Wojtek Figat
0670c0bbd3 Fix compilation warnings 2025-06-05 18:32:36 +02:00
Wojtek Figat
f462a2187f Merge branch 'master' into 1.11 2025-06-05 18:03:17 +02:00
Wojtek Figat
c2cbaeed30 Add option to change Character Controller capsule origin to start at feet location 2025-06-05 17:48:07 +02:00
Wojtek Figat
e982a23ed3 Add CharacterController.Resize for quick crouching implementation for characters 2025-06-05 15:07:51 +02:00
Wojtek Figat
f6feae5cf2 Optimize collider cached scale 2025-06-05 15:06:03 +02:00
Wojtek Figat
ba75fd5882 Fix crash regression when textbox watermak is null 2025-06-05 12:47:45 +02:00
Wojtek Figat
1eaf40f2f7 Fix changing CharacterController center at runtime to maintain actor placement 2025-06-05 12:40:47 +02:00
Wojtek Figat
fe98a23cb4 Merge branch 'GoaLitiuM-update_tracy' 2025-06-05 00:01:04 +02:00
Wojtek Figat
77bf03128e Merge branch 'update_tracy' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-update_tracy 2025-06-04 23:53:32 +02:00
Wojtek Figat
8f49a492d8 Fix duplicating json assets to properly remap object IDs
#3441
2025-06-04 23:49:19 +02:00
Chandler Cox
dd4bb2f7f1 Dont scroll to node when play is starting or ending. 2025-06-04 11:16:01 -05:00
Wojtek Figat
496856d12e Add shader header proxy for easy .hlsl files creation within Editor 2025-06-04 00:12:43 +02:00
6c63c2f650 Update tracy to 0.12.0 2025-06-02 20:00:06 +03:00
Wojtek Figat
8eff098850 Fix Linux build 2025-05-28 04:30:08 +02:00
Wojtek Figat
4fe9fdded6 Optimize redundant string allocation in managed binary module unload 2025-05-28 04:10:47 +02:00
Wojtek Figat
03d52d4eb9 Add support for building engine without logging 2025-05-28 04:05:12 +02:00
Wojtek Figat
ab61ed5a37 Add more memory profiling insights and groups 2025-05-28 04:03:44 +02:00
Wojtek Figat
72ee80242d Add integration with Tracy profiler to plot main memory categories 2025-05-26 05:37:53 +02:00
Wojtek Figat
9dc4dbc6d7 Add more memory profiler categories 2025-05-25 18:38:07 +02:00
Wojtek Figat
a74c5e7943 Another fix for iOS build 2025-05-25 18:01:30 +02:00
Wojtek Figat
8f9fa6995e Fix compilation issues 2025-05-25 17:40:00 +02:00
Wojtek Figat
98e59450f1 Add freeing managed assembly memory on reload/unload 2025-05-25 17:39:51 +02:00
Wojtek Figat
8c62f1120f Optimize dynamic memory allocations for managed runtime interop collections with a new Arena Allocation 2025-05-25 17:39:20 +02:00
Wojtek Figat
9aaba955d0 Fix profiler tables to use column headers aligned to center 2025-05-25 02:04:56 +02:00
Wojtek Figat
410ec0465c Optimize CSharp scripting runtime to use arena allocator per-assembly 2025-05-25 02:04:16 +02:00
Wojtek Figat
f9cb4ddae2 Add new Arena Allocator for optimized dynamic memory allocations with a shared lifetime 2025-05-24 05:08:32 +02:00
Wojtek Figat
bb855e2663 Add suport for Tracy profiler on Mac 2025-05-22 05:34:36 +02:00
Wojtek Figat
d24f9d1e1e Add warning when using memory profiler without enabled on startup 2025-05-22 05:18:56 +02:00
Wojtek Figat
c1b1f4afc4 Add process memory stats for Apple platforms 2025-05-22 04:49:48 +02:00
Wojtek Figat
c639a3103c Add memory profiling events to the main areas of the engine 2025-05-22 04:47:01 +02:00
Wojtek Figat
32bc73610f Fix debug command type detection when it's used with argument 2025-05-22 04:45:12 +02:00
Wojtek Figat
66dcfafa2e Fix Vulkan descriptor sets pooling 2025-05-22 04:42:01 +02:00
Wojtek Figat
9215f2662f Add missing memory alloc profiling for virtual pages 2025-05-22 04:41:01 +02:00
Wojtek Figat
2dc404cbd3 Add new memory profiler 2025-05-22 04:40:32 +02:00
7c5628d47e Fix crash caused by conflicting tracy DbgHelp lock helper names 2025-05-02 14:20:18 +03:00
9645008460 Update tracy to 0.11.1 2025-05-02 14:20:18 +03:00
507 changed files with 15900 additions and 3096 deletions

View File

@@ -28,6 +28,13 @@ TextureCube SkyLightTexture : register(t__SRV__);
Buffer<float4> ShadowsBuffer : register(t__SRV__);
Texture2D<float> ShadowMap : register(t__SRV__);
@4// Forward Shading: Utilities
// Public accessors for lighting data, use them as data binding might change but those methods will remain.
LightData GetDirectionalLight() { return DirectionalLight; }
LightData GetSkyLight() { return SkyLight; }
ProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
ExponentialHeightFogData GetExponentialHeightFog() { return ExponentialHeightFog; }
uint GetLocalLightsCount() { return LocalLightsCount; }
LightData GetLocalLight(uint i) { return LocalLights[i]; }
@5// Forward Shading: Shaders
// Pixel Shader function for Forward Pass
@@ -76,9 +83,8 @@ void PS_Forward(
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
// Calculate lighting from a single directional light
float4 shadowMask = 1.0f;
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
shadowMask = GetShadowMask(shadow);
float4 shadowMask = GetShadowMask(shadow);
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
// Calculate lighting from sky light
@@ -143,9 +149,9 @@ void PS_Forward(
#endif
#if USE_FOG
#if USE_FOG && MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT
// Calculate exponential height fog
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0);
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, gBuffer.ViewPos.z);
// Apply fog to the output color
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE

View File

@@ -15,6 +15,7 @@
#include "./Flax/Common.hlsl"
#include "./Flax/MaterialCommon.hlsl"
#include "./Flax/GBufferCommon.hlsl"
#include "./Flax/TerrainCommon.hlsl"
@7
// Primary constant buffer (with additional material parameters)
META_CB_BEGIN(0, Data)
@@ -334,7 +335,7 @@ VertexOutput VS(TerrainVertexInput input)
float lodValue = CurrentLOD;
float morphAlpha = lodCalculated - CurrentLOD;
// Sample heightmap
// Sample heightmap and splatmaps
float2 heightmapUVs = input.TexCoord * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw;
#if USE_SMOOTH_LOD_TRANSITION
float4 heightmapValueThisLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
@@ -342,7 +343,6 @@ VertexOutput VS(TerrainVertexInput input)
float2 heightmapUVsNextLOD = nextLODPos * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw;
float4 heightmapValueNextLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1);
float4 heightmapValue = lerp(heightmapValueThisLOD, heightmapValueNextLOD, morphAlpha);
bool isHole = max(heightmapValueThisLOD.b + heightmapValueThisLOD.a, heightmapValueNextLOD.b + heightmapValueNextLOD.a) >= 1.9f;
#if USE_TERRAIN_LAYERS
float4 splatmapValueThisLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
float4 splatmapValueNextLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1);
@@ -355,7 +355,6 @@ VertexOutput VS(TerrainVertexInput input)
#endif
#else
float4 heightmapValue = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
bool isHole = (heightmapValue.b + heightmapValue.a) >= 1.9f;
#if USE_TERRAIN_LAYERS
float4 splatmap0Value = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
#if TERRAIN_LAYERS_DATA_SIZE > 1
@@ -363,12 +362,11 @@ VertexOutput VS(TerrainVertexInput input)
#endif
#endif
#endif
float height = (float)((int)(heightmapValue.x * 255.0) + ((int)(heightmapValue.y * 255) << 8)) / 65535.0;
float height = DecodeHeightmapHeight(heightmapValue);
// Extract normal and the holes mask
float2 normalTemp = float2(heightmapValue.b, heightmapValue.a) * 2.0f - 1.0f;
float3 normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y);
normal = normalize(normal);
bool isHole;
float3 normal = DecodeHeightmapNormal(heightmapValue, isHole);
output.Geometry.HolesMask = isHole ? 0 : 1;
if (isHole)
{

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -2,9 +2,9 @@
"Name": "Flax",
"Version": {
"Major": 1,
"Minor": 10,
"Minor": 11,
"Revision": 0,
"Build": 6705
"Build": 6800
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",

View File

@@ -267,6 +267,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=comperand/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=coord/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cubemap/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=DDGI/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Deformer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=deformers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=defragmentation/@EntryIndexedValue">True</s:Boolean>

View File

@@ -14,4 +14,4 @@ bash ./Development/Scripts/Linux/CallBuildTool.sh --genproject "$@"
# Build bindings for all editor configurations
echo Building C# bindings...
# TODO: Detect the correct architecture here
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor
#Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor

View File

@@ -57,9 +57,9 @@ Follow the instructions below to compile and run the engine from source.
* Arch: `sudo pacman -S git git-lfs`
* `git-lfs install`
* Install the required packages:
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev zenity wayland-protocols libportal-dev`
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel zenity wayland-protocols-devel libportal`
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib zenity wayland-protocols libportal`
* Install Clang compiler (version 6 or later):
* Ubuntu: `sudo apt-get install clang lldb lld`
* Fedora: `sudo dnf install clang llvm lldb lld`

View File

@@ -174,7 +174,9 @@ void EditorAnalytics::StartSession()
// Bind events
GameCooker::OnEvent.Bind<RegisterGameCookingStart>();
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>();
#if LOG_ENABLE
Log::Logger::OnError.Bind<RegisterError>();
#endif
}
void EditorAnalytics::EndSession()
@@ -187,7 +189,9 @@ void EditorAnalytics::EndSession()
// Unbind events
GameCooker::OnEvent.Unbind<RegisterGameCookingStart>();
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>();
#if LOG_ENABLE
Log::Logger::OnError.Unbind<RegisterError>();
#endif
// End session
{

View File

@@ -10,11 +10,9 @@ using FlaxEngine;
namespace FlaxEditor.Content
{
/// <summary>
/// Context proxy object for shader source files (represented by <see cref="ShaderSourceItem"/>).
/// Base class for shader source files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
[ContentContextMenu("New/Shader Source")]
public class ShaderSourceProxy : ContentProxy
public abstract class ShaderBaseProxy : ContentProxy
{
/// <inheritdoc />
public override bool CanCreate(ContentFolder targetLocation)
@@ -29,6 +27,21 @@ namespace FlaxEditor.Content
return targetLocation.ShortName == "Source" && prevTargetLocation.ShortName == "Shaders";
}
/// <inheritdoc />
public override EditorWindow Open(Editor editor, ContentItem item)
{
Editor.Instance.CodeEditing.OpenFile(item.Path);
return null;
}
}
/// <summary>
/// Context proxy object for shader source files (represented by <see cref="ShaderSourceItem"/>).
/// </summary>
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
[ContentContextMenu("New/Shader Source (.shader)")]
public class ShaderSourceProxy : ShaderBaseProxy
{
/// <inheritdoc />
public override void Create(string outputPath, object arg)
{
@@ -44,13 +57,6 @@ namespace FlaxEditor.Content
File.WriteAllText(outputPath, shaderTemplate, Encoding.UTF8);
}
/// <inheritdoc />
public override EditorWindow Open(Editor editor, ContentItem item)
{
Editor.Instance.CodeEditing.OpenFile(item.Path);
return null;
}
/// <inheritdoc />
public override Color AccentColor => Color.FromRGB(0x7542f5);
@@ -66,4 +72,33 @@ namespace FlaxEditor.Content
return item is ShaderSourceItem;
}
}
/// <summary>
/// Context proxy object for shader header files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
[ContentContextMenu("New/Shader Header (.hlsl)")]
public class ShaderHeaderProxy : ShaderBaseProxy
{
/// <inheritdoc />
public override void Create(string outputPath, object arg)
{
File.WriteAllText(outputPath, "\n", Encoding.UTF8);
}
/// <inheritdoc />
public override Color AccentColor => Color.FromRGB(0x2545a5);
/// <inheritdoc />
public override string FileExtension => "hlsl";
/// <inheritdoc />
public override string Name => "Shader Header";
/// <inheritdoc />
public override bool IsProxyFor(ContentItem item)
{
return false;
}
}
}

View File

@@ -30,6 +30,7 @@
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
#include "Engine/Content/JsonAsset.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if PLATFORM_TOOLS_WINDOWS
#include "Platform/Windows/WindowsPlatformTools.h"
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
@@ -380,6 +381,7 @@ bool GameCooker::IsCancelRequested()
PlatformTools* GameCooker::GetTools(BuildPlatform platform)
{
PROFILE_MEM(Editor);
PlatformTools* result = nullptr;
if (!Tools.TryGet(platform, result))
{
@@ -471,6 +473,7 @@ bool GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
LOG(Error, "Build platform {0} is not supported.", ::ToString(platform));
return true;
}
PROFILE_MEM(Editor);
// Setup
CancelFlag = 0;
@@ -624,6 +627,7 @@ void GameCookerImpl::ReportProgress(const String& info, float totalProgress)
void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
{
PROFILE_MEM(Editor);
if (Internal_OnCollectAssets == nullptr)
{
auto c = GameCooker::GetStaticClass();
@@ -651,6 +655,7 @@ void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
bool GameCookerImpl::Build()
{
PROFILE_MEM(Editor);
CookingData& data = *Data;
LOG(Info, "Starting Game Cooker...");
LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration));
@@ -778,6 +783,8 @@ int32 GameCookerImpl::ThreadFunction()
bool GameCookerService::Init()
{
PROFILE_MEM(Editor);
auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading);
GameCooker::OnCollectAssets.Bind(OnCollectAssets);
@@ -789,6 +796,7 @@ void GameCookerService::Update()
{
if (IsRunning)
{
PROFILE_MEM(Editor);
ScopeLock lock(ProgressLocker);
if (ProgressMsg.HasChars())

View File

@@ -186,7 +186,7 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
ADD_ENTRY("CFBundlePackageType", "APPL");
ADD_ENTRY("NSPrincipalClass", "NSApplication");
ADD_ENTRY("LSApplicationCategoryType", "public.app-category.games");
ADD_ENTRY("LSMinimumSystemVersion", "10.15");
ADD_ENTRY("LSMinimumSystemVersion", "13");
ADD_ENTRY("CFBundleIconFile", "icon.icns");
ADD_ENTRY_STR("CFBundleExecutable", executableName);
ADD_ENTRY_STR("CFBundleIdentifier", appIdentifier);

View File

@@ -36,6 +36,7 @@
#include "Engine/Engine/Base/GameBase.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Enums.h"
#if PLATFORM_TOOLS_WINDOWS

View File

@@ -6,6 +6,8 @@
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Types/Stopwatch.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h"
@@ -69,6 +71,7 @@ MTypeObject* CustomEditorsUtil::GetCustomEditor(MTypeObject* refType)
bool CustomEditorsUtilService::Init()
{
PROFILE_MEM(Editor);
TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly);
Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded);
@@ -77,6 +80,8 @@ bool CustomEditorsUtilService::Init()
void OnAssemblyLoaded(MAssembly* assembly)
{
PROFILE_CPU_NAMED("CustomEditors.OnAssemblyLoaded");
PROFILE_MEM(Editor);
Stopwatch stopwatch;
// Prepare FlaxEngine

View File

@@ -1,6 +1,5 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using FlaxEditor.Content.Settings;
using FlaxEngine;
@@ -16,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
private int _layersCount;
private List<CheckBox> _checkBoxes;
private VerticalPanel _upperRightCell;
private VerticalPanel _bottomLeftCell;
private UniformGridPanel _grid;
private Border _horizontalHighlight;
private Border _verticalHighlight;
/// <inheritdoc />
public override DisplayStyle Style => DisplayStyle.InlineIntoParent;
@@ -37,12 +41,29 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = panel,
};
var style = FlaxEngine.GUI.Style.Current;
_horizontalHighlight = new Border()
{
Parent = panel,
BorderColor = style.Foreground,
BorderWidth = 1.0f,
Visible = false,
};
_verticalHighlight = new Border()
{
Parent = panel,
BorderColor = style.Foreground,
BorderWidth = 1.0f,
Visible = false,
};
var upperLeftCell = new Label
{
Parent = gridPanel,
};
var upperRightCell = new VerticalPanel
_upperRightCell = new VerticalPanel
{
ClipChildren = false,
Pivot = new Float2(0.00001f, 0.0f),
@@ -54,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = gridPanel,
};
var bottomLeftCell = new VerticalPanel
_bottomLeftCell = new VerticalPanel
{
Pivot = Float2.Zero,
Spacing = 0,
@@ -63,7 +84,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = gridPanel,
};
var grid = new UniformGridPanel(0)
_grid = new UniformGridPanel(0)
{
SlotsHorizontally = layersCount,
SlotsVertically = layersCount,
@@ -74,13 +95,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
int layerIndex = 0;
for (; layerIndex < layerNames.Length; layerIndex++)
{
upperRightCell.AddChild(new Label
_upperRightCell.AddChild(new Label
{
Height = labelsHeight,
Text = layerNames[layerNames.Length - layerIndex - 1],
HorizontalAlignment = TextAlignment.Near,
});
bottomLeftCell.AddChild(new Label
_bottomLeftCell.AddChild(new Label
{
Height = labelsHeight,
Text = layerNames[layerIndex],
@@ -90,13 +111,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
for (; layerIndex < layersCount; layerIndex++)
{
string name = "Layer " + layerIndex;
upperRightCell.AddChild(new Label
_upperRightCell.AddChild(new Label
{
Height = labelsHeight,
Text = name,
HorizontalAlignment = TextAlignment.Near,
});
bottomLeftCell.AddChild(new Label
_bottomLeftCell.AddChild(new Label
{
Height = labelsHeight,
Text = name,
@@ -118,7 +139,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
var box = new CheckBox(0, 0, true)
{
Tag = new Float2(_layersCount - column - 1, row),
Parent = grid,
Parent = _grid,
Checked = GetBit(column, row),
};
box.StateChanged += OnCheckBoxChanged;
@@ -126,7 +147,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
}
for (; column < layersCount; column++)
{
grid.AddChild(new Label());
_grid.AddChild(new Label());
}
}
}
@@ -141,6 +162,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// <inheritdoc />
public override void Refresh()
{
int selectedColumn = -1;
int selectedRow = -1;
var style = FlaxEngine.GUI.Style.Current;
bool mouseOverGrid = _grid.IsMouseOver;
// Only hide highlights if mouse is not over the grid to reduce flickering
if (!mouseOverGrid)
{
_horizontalHighlight.Visible = false;
_verticalHighlight.Visible = false;
}
// Sync check boxes
for (int i = 0; i < _checkBoxes.Count; i++)
{
@@ -148,6 +181,39 @@ namespace FlaxEditor.CustomEditors.Dedicated
int column = (int)((Float2)box.Tag).X;
int row = (int)((Float2)box.Tag).Y;
box.Checked = GetBit(column, row);
if (box.IsMouseOver)
{
selectedColumn = column;
selectedRow = row;
_horizontalHighlight.X = _grid.X - _bottomLeftCell.Width;
_horizontalHighlight.Y = _grid.Y + box.Y;
_horizontalHighlight.Width = _bottomLeftCell.Width + box.Width + box.X;
_horizontalHighlight.Height = box.Height;
_horizontalHighlight.Visible = true;
_verticalHighlight.X = _grid.X + box.X;
_verticalHighlight.Y = _grid.Y - _upperRightCell.Height;
_verticalHighlight.Width = box.Width;
_verticalHighlight.Height = _upperRightCell.Height + box.Height + box.Y;
_verticalHighlight.Visible = true;
}
}
for (int i = 0; i < _checkBoxes.Count; i++)
{
var box = _checkBoxes[i];
int column = (int)((Float2)box.Tag).X;
int row = (int)((Float2)box.Tag).Y;
if (!mouseOverGrid)
box.ImageColor = style.BorderSelected;
else if (selectedColumn > -1 && selectedRow > -1)
{
bool isRowOrColumn = column == selectedColumn || row == selectedRow;
box.ImageColor = style.BorderSelected * (isRowOrColumn ? 1.2f : 0.75f);
}
}
}

View File

@@ -26,6 +26,7 @@ namespace FlaxEditor.CustomEditors.Editors
new OptionType("Texture 9-Slicing", typeof(Texture9SlicingBrush)),
new OptionType("Sprite 9-Slicing", typeof(Sprite9SlicingBrush)),
new OptionType("Video", typeof(VideoBrush)),
new OptionType("UI Brush", typeof(UIBrush)),
};
}
}

View File

@@ -604,6 +604,9 @@ namespace FlaxEditor.CustomEditors.Editors
root.SortChildrenRecursive();
root.Expand(true);
if (Input.GetKey(KeyboardKeys.Shift))
root.ExpandAll(true);
return menu;
}
}

View File

@@ -287,10 +287,7 @@ namespace FlaxEditor.CustomEditors
/// <returns>The created element.</returns>
public ImageElement Image(SpriteHandle sprite)
{
var element = new ImageElement();
element.Image.Brush = new SpriteBrush(sprite);
OnAddElement(element);
return element;
return Image(new SpriteBrush(sprite));
}
/// <summary>
@@ -300,10 +297,7 @@ namespace FlaxEditor.CustomEditors
/// <returns>The created element.</returns>
public ImageElement Image(Texture texture)
{
var element = new ImageElement();
element.Image.Brush = new TextureBrush(texture);
OnAddElement(element);
return element;
return Image(new TextureBrush(texture));
}
/// <summary>
@@ -312,9 +306,19 @@ namespace FlaxEditor.CustomEditors
/// <param name="texture">The GPU texture.</param>
/// <returns>The created element.</returns>
public ImageElement Image(GPUTexture texture)
{
return Image(new GPUTextureBrush(texture));
}
/// <summary>
/// Adds brush image to the layout.
/// </summary>
/// <param name="brush">The brush.</param>
/// <returns>The created element.</returns>
public ImageElement Image(IBrush brush)
{
var element = new ImageElement();
element.Image.Brush = new GPUTextureBrush(texture);
element.Image.Brush = brush;
OnAddElement(element);
return element;
}

View File

@@ -20,6 +20,7 @@
#include "Engine/Engine/Engine.h"
#include "Engine/ShadowsOfMordor/Builder.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h"
#if PLATFORM_LINUX
#include "Engine/Tools/TextureTool/TextureTool.h"
@@ -47,6 +48,7 @@ void Editor::CloseSplashScreen()
bool Editor::CheckProjectUpgrade()
{
PROFILE_MEM(Editor);
const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version");
// Load version cache file
@@ -366,6 +368,8 @@ bool Editor::BackupProject()
int32 Editor::LoadProduct()
{
PROFILE_MEM(Editor);
// Flax Editor product
Globals::ProductName = TEXT("Flax Editor");
Globals::CompanyName = TEXT("Flax");
@@ -626,6 +630,7 @@ int32 Editor::LoadProduct()
Window* Editor::CreateMainWindow()
{
PROFILE_MEM(Editor);
Window* window = Managed->GetMainWindow();
#if PLATFORM_LINUX
@@ -662,6 +667,7 @@ bool Editor::Init()
return true;
}
PROFILE_CPU();
PROFILE_MEM(Editor);
// If during last lightmaps baking engine crashed we could try to restore the progress
ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState();
@@ -693,11 +699,13 @@ bool Editor::Init()
void Editor::BeforeRun()
{
PROFILE_MEM(Editor);
Managed->BeforeRun();
}
void Editor::BeforeExit()
{
PROFILE_MEM(Editor);
CloseSplashScreen();
Managed->Exit();
@@ -708,6 +716,8 @@ void Editor::BeforeExit()
void EditorImpl::OnUpdate()
{
PROFILE_MEM(Editor);
// Update c# editor
Editor::Managed->Update();

View File

@@ -670,6 +670,8 @@ namespace FlaxEditor
{
FlaxEngine.Networking.NetworkManager.Stop(); // Shutdown any multiplayer from playmode
PlayModeEnding?.Invoke();
for (int i = 0; i < _modules.Count; i++)
_modules[i].OnPlayEnding();
}
internal void OnPlayEnd()

View File

@@ -299,6 +299,7 @@ namespace FlaxEditor.GUI
{
// Select asset
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
}
}
else if (Button1Rect.Contains(location))
@@ -312,6 +313,7 @@ namespace FlaxEditor.GUI
{
// Select asset
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
}
else if (Button3Rect.Contains(location))
{

View File

@@ -51,7 +51,7 @@ namespace FlaxEditor.GUI
/// <summary>
/// The column title horizontal text alignment
/// </summary>
public TextAlignment TitleAlignment = TextAlignment.Near;
public TextAlignment TitleAlignment = TextAlignment.Center;
/// <summary>
/// The column title margin.

View File

@@ -326,8 +326,11 @@ namespace FlaxEditor.GUI.Dialogs
// Update eye dropper tool
if (_activeEyedropper)
{
// Try reading the color under the cursor in realtime if supported by the platform
Float2 mousePosition = Platform.MousePosition;
SelectedColor = ScreenUtilities.GetColorAt(mousePosition);
Color color = ScreenUtilities.GetColorAt(mousePosition);
if (color != Color.Transparent)
SelectedColor = color;
}
}

View File

@@ -11,6 +11,42 @@ namespace FlaxEditor.GUI.Docking
/// <seealso cref="DockPanel" />
public class FloatWindowDockPanel : DockPanel
{
private class FloatWindowDecorations : WindowDecorations
{
private FloatWindowDockPanel _panel;
public FloatWindowDecorations(FloatWindowDockPanel panel)
: base(panel.RootWindow)
{
_panel = panel;
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
{
_panel.BeginDrag();
return true;
}
return base.OnMouseDown(location, button);
}
#if !PLATFORM_WINDOWS
/// <inheritdoc />
protected override WindowHitCodes OnHitTest(ref Float2 mouse)
{
var hit = base.OnHitTest(ref mouse);
if (hit == WindowHitCodes.Caption)
{
// Override the system behaviour when interacting with the caption area
hit = WindowHitCodes.Client;
}
return hit;
}
#endif
}
private MasterDockPanel _masterPanel;
private WindowRootControl _window;
@@ -40,6 +76,26 @@ namespace FlaxEditor.GUI.Docking
Parent = window;
_window.Window.Closing += OnClosing;
_window.Window.LeftButtonHit += OnLeftButtonHit;
if (Utilities.Utils.UseCustomWindowDecorations())
{
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
}
}
/// <inheritdoc />
protected override void PerformLayoutBeforeChildren()
{
base.PerformLayoutBeforeChildren();
var decorations = Parent.GetChild<FloatWindowDecorations>();
if (decorations != null)
{
// Apply offset for the title bar
foreach (var child in Children)
child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height };
}
}
/// <summary>
@@ -87,6 +143,12 @@ namespace FlaxEditor.GUI.Docking
settings.ShowAfterFirstPaint = false;
settings.ShowInTaskbar = true;
settings.StartPosition = startPosition;
if (Utilities.Utils.UseCustomWindowDecorations())
{
settings.HasBorder = false;
//settings.HasSizingFrame = false;
}
// Create window
return Platform.CreateWindow(ref settings);

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;

View File

@@ -227,9 +227,8 @@ namespace FlaxEditor.GUI
{
int order = -1 * SortScore.CompareTo(otherItem.SortScore);
if (order == 0)
{
order = string.Compare(Name, otherItem.Name, StringComparison.Ordinal);
}
return order;
}
return base.Compare(other);
@@ -509,7 +508,7 @@ namespace FlaxEditor.GUI
OnSearchFilterChanged();
}
private List<Item> GetVisibleItems()
private List<Item> GetVisibleItems(bool ignoreFoldedCategories)
{
var result = new List<Item>();
var items = ItemsPanel.Children;
@@ -523,7 +522,7 @@ namespace FlaxEditor.GUI
for (int i = 0; i < _categoryPanels.Count; i++)
{
var category = _categoryPanels[i];
if (!category.Visible)
if (!category.Visible || (ignoreFoldedCategories && category is DropPanel panel && panel.IsClosed))
continue;
for (int j = 0; j < category.Children.Count; j++)
{
@@ -535,6 +534,12 @@ namespace FlaxEditor.GUI
return result;
}
private void ExpandToItem(Item item)
{
if (item.Parent is DropPanel dropPanel)
dropPanel.Open(false);
}
/// <inheritdoc />
protected override void OnShow()
{
@@ -563,8 +568,17 @@ namespace FlaxEditor.GUI
case KeyboardKeys.Escape:
Hide();
return true;
case KeyboardKeys.Backspace:
// Allow the user to quickly focus the searchbar
if (_searchBox != null && !_searchBox.IsFocused)
{
_searchBox.Focus();
_searchBox.SelectAll();
return true;
}
break;
case KeyboardKeys.ArrowDown:
{
case KeyboardKeys.ArrowUp:
if (RootWindow.FocusedControl == null)
{
// Focus search box if nothing is focused
@@ -572,39 +586,28 @@ namespace FlaxEditor.GUI
return true;
}
// Focus the first visible item or then next one
var items = GetVisibleItems();
// Get the next item
bool controlDown = Root.GetKey(KeyboardKeys.Control);
var items = GetVisibleItems(!controlDown);
var focusedIndex = items.IndexOf(focusedItem);
if (focusedIndex == -1)
focusedIndex = -1;
if (focusedIndex + 1 < items.Count)
{
var item = items[focusedIndex + 1];
item.Focus();
_scrollPanel.ScrollViewTo(item);
return true;
}
break;
}
case KeyboardKeys.ArrowUp:
if (focusedItem != null)
{
// Focus the previous visible item or the search box
var items = GetVisibleItems();
var focusedIndex = items.IndexOf(focusedItem);
if (focusedIndex == 0)
{
_searchBox?.Focus();
}
else if (focusedIndex > 0)
{
var item = items[focusedIndex - 1];
item.Focus();
_scrollPanel.ScrollViewTo(item);
return true;
}
}
break;
// If the user hasn't selected anything yet and is holding control, focus first folded item
if (focusedIndex == -1 && controlDown)
focusedIndex = GetVisibleItems(true).Count - 1;
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, items.Count - 1);
var nextItem = items[nextIndex];
// Focus the next item
nextItem.Focus();
// Allow the user to expand groups while scrolling
if (controlDown)
ExpandToItem(nextItem);
_scrollPanel.ScrollViewTo(nextItem);
return true;
case KeyboardKeys.Return:
if (focusedItem != null)
{
@@ -614,7 +617,7 @@ namespace FlaxEditor.GUI
else
{
// Select first item if no item is focused (most likely to be the best result), saves the user from pressing arrow down first
var visibleItems = GetVisibleItems();
var visibleItems = GetVisibleItems(true);
if (visibleItems.Count > 0)
{
OnClickItem(visibleItems[0]);

View File

@@ -12,16 +12,6 @@ namespace FlaxEditor.GUI
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
public sealed class MainMenu : ContainerControl
{
#if PLATFORM_WINDOWS || PLATFORM_SDL
private bool _useCustomWindowSystem;
private Image _icon;
private Label _title;
private Button _closeButton;
private Button _minimizeButton;
private Button _maximizeButton;
private LocalizedString _charChromeRestore, _charChromeMaximize;
private Window _window;
#endif
private MainMenuButton _selected;
/// <summary>
@@ -60,202 +50,12 @@ namespace FlaxEditor.GUI
/// <summary>
/// Initializes a new instance of the <see cref="MainMenu"/> class.
/// </summary>
/// <param name="mainWindow">The main window.</param>
public MainMenu(RootControl mainWindow)
public MainMenu()
: base(0, 0, 0, 20)
{
AutoFocus = false;
AnchorPreset = AnchorPresets.HorizontalStretchTop;
#if PLATFORM_WINDOWS || PLATFORM_SDL
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
if (_useCustomWindowSystem)
{
BackgroundColor = Style.Current.LightBackground;
Height = 28;
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
Font iconFont = windowIconsFont?.CreateFont(9);
_window = mainWindow.RootWindow.Window;
_window.HitTest += OnHitTest;
_window.Closed += OnWindowClosed;
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
var driver = Platform.DisplayServer;
_icon = new Image
{
Margin = new Margin(6, 6, 6, 6),
Brush = new TextureBrush(windowIcon),
Color = Style.Current.Foreground,
KeepAspectRatio = false,
TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2} {4}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration, driver),
Parent = this,
};
_title = new Label(0, 0, Width, Height)
{
Text = _window.Title,
HorizontalAlignment = TextAlignment.Center,
VerticalAlignment = TextAlignment.Center,
ClipText = true,
TextColor = Style.Current.ForegroundGrey,
TextColorHighlighted = Style.Current.ForegroundGrey,
Parent = this,
};
_closeButton = new Button
{
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Color.Transparent,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Color.Red,
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
Parent = this,
};
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
_minimizeButton = new Button
{
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Color.Transparent,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
Parent = this,
};
_minimizeButton.Clicked += () => _window.Minimize();
_maximizeButton = new Button
{
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Color.Transparent,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
Parent = this,
};
_maximizeButton.Clicked += () =>
{
if (_window.IsMaximized)
_window.Restore();
else
_window.Maximize();
};
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
}
else
#endif
{
BackgroundColor = Style.Current.LightBackground;
}
}
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <inheritdoc />
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (_maximizeButton != null)
{
_maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
}
}
private void OnWindowClosed()
{
if (_window != null)
{
_window.HitTest = null;
_window = null;
}
}
private WindowHitCodes OnHitTest(ref Float2 mouse)
{
var dpiScale = _window.DpiScale;
if (_window.IsMinimized)
return WindowHitCodes.NoWhere;
if (!_window.IsMaximized)
{
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
var winSize = _window.Size;
// Distance from which the mouse is considered to be on the border/corner
float distance = 5.0f * dpiScale;
if (pos.Y > winSize.Y - distance && pos.X < distance)
return WindowHitCodes.BottomLeft;
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
return WindowHitCodes.BottomRight;
if (pos.Y < distance && pos.X < distance)
return WindowHitCodes.TopLeft;
if (pos.Y < distance && pos.X > winSize.X - distance)
return WindowHitCodes.TopRight;
if (pos.X > winSize.X - distance)
return WindowHitCodes.Right;
if (pos.X < distance)
return WindowHitCodes.Left;
if (pos.Y < distance)
return WindowHitCodes.Top;
if (pos.Y > winSize.Y - distance)
return WindowHitCodes.Bottom;
}
var mousePos = PointFromScreen(mouse * dpiScale);
var controlUnderMouse = GetChildAt(mousePos);
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
var rb = GetRightButton();
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight, _minimizeButton.BottomLeft - rb.UpperRight).Contains(ref mousePos) && !isMouseOverSth)
return WindowHitCodes.Caption;
return WindowHitCodes.Client;
}
#endif
/// <summary>
/// Return the rightmost button.
/// </summary>
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
private MainMenuButton GetRightButton()
{
MainMenuButton b = null;
foreach (var control in Children)
{
if (b == null && control is MainMenuButton)
b = (MainMenuButton)control;
if (control is MainMenuButton && control.Right > b.Right)
b = (MainMenuButton)control;
}
return b;
BackgroundColor = Style.Current.LightBackground;
}
/// <summary>
@@ -268,6 +68,19 @@ namespace FlaxEditor.GUI
return AddChild(new MainMenuButton(text));
}
/// <summary>
/// Gets or adds a button.
/// </summary>
/// <param name="text">The button text</param>
/// <returns>The existing or created button control.</returns>
public MainMenuButton GetOrAddButton(string text)
{
MainMenuButton result = GetButton(text);
if (result == null)
result = AddButton(text);
return result;
}
/// <summary>
/// Gets the button.
/// </summary>
@@ -287,26 +100,6 @@ namespace FlaxEditor.GUI
return result;
}
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
if (base.OnMouseDoubleClick(location, button))
return true;
#if PLATFORM_WINDOWS || PLATFORM_SDL
var child = GetChildAtRecursive(location);
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
{
if (_window.IsMaximized)
_window.Restore();
else
_window.Maximize();
}
#endif
return true;
}
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
@@ -322,16 +115,8 @@ namespace FlaxEditor.GUI
protected override void PerformLayoutAfterChildren()
{
float x = 0;
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (_useCustomWindowSystem)
{
// Icon
_icon.X = x;
_icon.Size = new Float2(Height);
x += _icon.Width;
}
#endif
WindowDecorations decorations = Parent.GetChild<WindowDecorations>();
x += decorations?.Icon?.Width ?? 0;
// Arrange controls
MainMenuButton rightMostButton = null;
@@ -350,37 +135,21 @@ namespace FlaxEditor.GUI
x += b.Width;
}
}
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (_useCustomWindowSystem)
{
// Buttons
_closeButton.Height = Height;
_closeButton.X = Width - _closeButton.Width;
_maximizeButton.Height = Height;
_maximizeButton.X = _closeButton.X - _maximizeButton.Width;
_minimizeButton.Height = Height;
_minimizeButton.X = _maximizeButton.X - _minimizeButton.Width;
// Title
_title.Bounds = new Rectangle(x + 2, 0, _minimizeButton.Left - x - 4, Height);
//_title.Text = _title.Width < 300.0f ? Editor.Instance.ProjectInfo.Name : _window.Title;
}
#endif
// Fill the right side if title and buttons are not present
if (decorations?.Title == null)
Width = Parent.Width;
else
Width = x;
}
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <inheritdoc />
public override void OnDestroy()
{
base.OnDestroy();
if (_window != null)
{
_window.Closed -= OnWindowClosed;
OnWindowClosed();
}
if (_selected != null)
Selected = null;
}
#endif
}
}

View File

@@ -42,14 +42,12 @@ namespace FlaxEditor.GUI
Text = text;
var style = Style.Current;
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
if (!Utilities.Utils.UseCustomWindowDecorations())
{
BackgroundColorMouseOver = style.BackgroundHighlighted;
BackgroundColorMouseOverOpened = style.Background;
}
else
#endif
{
BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f;
}

View File

@@ -122,6 +122,14 @@ namespace FlaxEditor.GUI
return this;
}
private void OnClicked()
{
if (AutoCheck)
Checked = !Checked;
Clicked?.Invoke();
(Parent as ToolStrip)?.OnButtonClicked(this);
}
/// <inheritdoc />
public override void Draw()
{
@@ -196,11 +204,7 @@ namespace FlaxEditor.GUI
if (button == MouseButton.Left && _primaryMouseDown)
{
_primaryMouseDown = false;
if (AutoCheck)
Checked = !Checked;
Clicked?.Invoke();
(Parent as ToolStrip)?.OnButtonClicked(this);
OnClicked();
return true;
}
if (button == MouseButton.Right && _secondaryMouseDown)
@@ -215,6 +219,18 @@ namespace FlaxEditor.GUI
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
if (button == MouseButton.Left)
{
OnClicked();
return true;
}
return false;
}
/// <inheritdoc />
public override void OnMouseLeave()
{

View File

@@ -73,6 +73,11 @@ namespace FlaxEditor.GUI.Tree
/// </summary>
public bool DrawRootTreeLine = true;
/// <summary>
/// Occurs when the deferred layout operation was performed.
/// </summary>
public event Action AfterDeferredLayout;
/// <summary>
/// Gets or sets the margin for the child tree nodes.
/// </summary>
@@ -375,6 +380,7 @@ namespace FlaxEditor.GUI.Tree
if (_deferLayoutUpdate)
{
base.PerformLayout();
AfterDeferredLayout?.Invoke();
_deferLayoutUpdate = false;
}

View File

@@ -0,0 +1,342 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.GUI.Docking;
using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI;
/// <summary>
/// Represents the title bar of the window with buttons.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
public class WindowDecorations : ContainerControl
{
private Image _icon;
private Label _title;
private Button _closeButton;
private Button _minimizeButton;
private Button _maximizeButton;
private LocalizedString _charChromeRestore, _charChromeMaximize;
private Window _window;
/// <summary>
/// The title label in the title bar.
/// </summary>
public Label Title => _title;
/// <summary>
/// The icon used in the title bar.
/// </summary>
public Image Icon => _icon;
/// <summary>
/// The tooltip shown when hovering over the icon.
/// </summary>
public string IconTooltipText
{
get => _icon?.TooltipText ?? null;
set
{
if (_icon != null)
_icon.TooltipText = value;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="WindowDecorations"/> class.
/// </summary>
/// <param name="window">The window.</param>
/// <param name="iconOnly">When set, omit drawing title and buttons.</param>
public WindowDecorations(RootControl window, bool iconOnly = false)
: base(0, 0, 0, 20)
{
_window = window.RootWindow.Window;
AutoFocus = false;
AnchorPreset = AnchorPresets.HorizontalStretchTop;
BackgroundColor = Color.Transparent;
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
_icon = new Image
{
Margin = new Margin(4, 4, 4, 4),
Brush = new TextureBrush(windowIcon),
Color = Style.Current.Foreground,
BackgroundColor = Style.Current.LightBackground,
KeepAspectRatio = false,
Parent = this,
};
if (!iconOnly)
{
_icon.Margin = new Margin(6, 6, 6, 6);
Height = 28;
_window.HitTest += OnHitTest;
_window.Closed += OnWindowClosed;
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
Font iconFont = windowIconsFont?.CreateFont(9);
_title = new Label(0, 0, Width, Height)
{
Text = _window.Title,
HorizontalAlignment = TextAlignment.Center,
VerticalAlignment = TextAlignment.Center,
ClipText = true,
TextColor = Style.Current.ForegroundGrey,
TextColorHighlighted = Style.Current.ForegroundGrey,
BackgroundColor = Style.Current.LightBackground,
Parent = this,
};
_closeButton = new Button
{
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Style.Current.LightBackground,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Color.Red,
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
Parent = this,
};
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
_minimizeButton = new Button
{
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Style.Current.LightBackground,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
Parent = this,
};
_minimizeButton.Clicked += () => _window.Minimize();
_maximizeButton = new Button
{
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
Font = new FontReference(iconFont),
BackgroundColor = Style.Current.LightBackground,
BorderColor = Color.Transparent,
BorderColorHighlighted = Color.Transparent,
BorderColorSelected = Color.Transparent,
TextColor = Style.Current.Foreground,
Width = 46,
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
Parent = this,
};
_maximizeButton.Clicked += () =>
{
if (_window.IsMaximized)
_window.Restore();
else
_window.Maximize();
};
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
}
}
/// <inheritdoc />
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (_maximizeButton != null)
{
var maximizeText = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
if (_maximizeButton.Text != maximizeText)
_maximizeButton.Text = maximizeText;
}
}
private void OnWindowClosed()
{
if (_window != null)
{
_window.HitTest -= OnHitTest;
_window = null;
}
}
/// <summary>
/// Perform hit test on the window.
/// </summary>
/// <param name="mouse">The mouse position</param>
/// <returns>The hit code for given position.</returns>
protected virtual WindowHitCodes OnHitTest(ref Float2 mouse)
{
if (_window.IsMinimized)
return WindowHitCodes.NoWhere;
var dpiScale = _window.DpiScale;
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
if (!_window.IsMaximized)
{
var winSize = _window.Size;
// Distance from which the mouse is considered to be on the border/corner
float distance = 5.0f * dpiScale;
if (pos.Y > winSize.Y - distance && pos.X < distance)
return WindowHitCodes.BottomLeft;
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
return WindowHitCodes.BottomRight;
if (pos.Y < distance && pos.X < distance)
return WindowHitCodes.TopLeft;
if (pos.Y < distance && pos.X > winSize.X - distance)
return WindowHitCodes.TopRight;
if (pos.X > winSize.X - distance)
return WindowHitCodes.Right;
if (pos.X < distance)
return WindowHitCodes.Left;
if (pos.Y < distance)
return WindowHitCodes.Top;
if (pos.Y > winSize.Y - distance)
return WindowHitCodes.Bottom;
}
var controlUnderMouse = GetChildAt(pos, control => control != _title);
if (_title.Bounds.Contains(pos) && controlUnderMouse == null)
return WindowHitCodes.Caption;
return WindowHitCodes.Client;
}
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
// These may not work with main window due to SDL not passing mouse events
// when interacting with hit tests on caption area...
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
{
if (_window.IsMaximized)
_window.Restore();
else
_window.Maximize();
return true;
}
else if (Icon.Bounds.Contains(location) && button == MouseButton.Left)
{
_window.Close(ClosingReason.User);
return true;
}
return base.OnMouseDoubleClick(location, button);
}
/// <inheritdoc />
protected override void PerformLayoutAfterChildren()
{
// Calculate extents for title bounds area excluding the icon and main menu area
float x = 0;
// Icon
if (_icon != null)
{
_icon.X = x;
_icon.Size = new Float2(Height);
x += _icon.Width;
}
// Main menu if present
if (Parent.GetChild<MainMenu>() is MainMenu mainMenu)
{
for (int i = 0; i < mainMenu.Children.Count; i++)
{
var c = mainMenu.Children[i];
if (c is MainMenuButton b && c.Visible)
{
b.Bounds = new Rectangle(x, 0, b.Width, Height);
x += b.Width;
}
}
}
// Buttons
float rightMostButtonX = Width;
if (_closeButton != null)
{
_closeButton.Height = Height;
_closeButton.X = rightMostButtonX - _closeButton.Width;
rightMostButtonX = _closeButton.X;
}
if (_maximizeButton != null)
{
_maximizeButton.Height = Height;
_maximizeButton.X = rightMostButtonX - _maximizeButton.Width;
rightMostButtonX = _maximizeButton.X;
}
if (_minimizeButton != null)
{
_minimizeButton.Height = Height;
_minimizeButton.X = rightMostButtonX - _minimizeButton.Width;
rightMostButtonX = _minimizeButton.X;
}
// Title
if (_title != null)
{
_title.Text = _window.Title;
_title.Bounds = new Rectangle(x, 0, rightMostButtonX - x, Height);
}
}
/// <inheritdoc />
public override void Draw()
{
base.Draw();
DrawBorders();
}
/// <summary>
/// Draw borders around the window.
/// </summary>
public virtual void DrawBorders()
{
var win = RootWindow.Window;
if (win.IsMaximized)
return;
if (Editor.Instance.UI.StatusBar == null)
return;
const float thickness = 1.0f;
Color color = Editor.Instance.UI.StatusBar.StatusColor;
Rectangle rect = new Rectangle(thickness * 0.5f, thickness * 0.5f, Parent.Width - thickness, Parent.Height - thickness);
Render2D.DrawRectangle(rect, color);
}
/// <inheritdoc />
public override void OnDestroy()
{
base.OnDestroy();
if (_window != null)
{
_window.Closed -= OnWindowClosed;
OnWindowClosed();
}
}
}

View File

@@ -13,6 +13,7 @@
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h"
#include "Engine/Content/Assets/VisualScript.h"
#include "Engine/Content/Content.h"
#include "Engine/Level/Actor.h"
#include "Engine/CSG/CSGBuilder.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Renderer/ProbesRenderer.h"
@@ -74,7 +75,7 @@ void OnLightmapsBuildFinished(bool failed)
OnLightmapsBake(ShadowsOfMordor::BuildProgressStep::GenerateLightmapCharts, 0, 0, false);
}
void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
void OnBakeEvent(bool started, Actor* e)
{
if (Internal_EnvProbeBake == nullptr)
{
@@ -82,7 +83,7 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
ASSERT(Internal_EnvProbeBake);
}
MObject* probeObj = e.Actor ? e.Actor->GetManagedInstance() : nullptr;
MObject* probeObj = e ? e->GetManagedInstance() : nullptr;
MainThreadManagedInvokeAction::ParamsBuilder params;
params.AddParam(started);
@@ -90,12 +91,12 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params);
}
void OnRegisterBake(const ProbesRenderer::Entry& e)
void OnRegisterBake(Actor* e)
{
OnBakeEvent(true, e);
}
void OnFinishBake(const ProbesRenderer::Entry& e)
void OnFinishBake(Actor* e)
{
OnBakeEvent(false, e);
}
@@ -156,7 +157,9 @@ ManagedEditor::ManagedEditor()
lightmapsBuilder->OnBuildProgress.Bind<OnLightmapsBuildProgress>();
lightmapsBuilder->OnBuildFinished.Bind<OnLightmapsBuildFinished>();
CSG::Builder::OnBrushModified.Bind<OnBrushModified>();
#if LOG_ENABLE
Log::Logger::OnMessage.Bind<OnLogMessage>();
#endif
VisualScripting::DebugFlow.Bind<OnVisualScriptingDebugFlow>();
}
@@ -172,7 +175,9 @@ ManagedEditor::~ManagedEditor()
lightmapsBuilder->OnBuildProgress.Unbind<OnLightmapsBuildProgress>();
lightmapsBuilder->OnBuildFinished.Unbind<OnLightmapsBuildFinished>();
CSG::Builder::OnBrushModified.Unbind<OnBrushModified>();
#if LOG_ENABLE
Log::Logger::OnMessage.Unbind<OnLogMessage>();
#endif
VisualScripting::DebugFlow.Unbind<OnVisualScriptingDebugFlow>();
}

View File

@@ -1135,6 +1135,7 @@ namespace FlaxEditor.Modules
Proxy.Add(new FontProxy());
Proxy.Add(new ShaderProxy());
Proxy.Add(new ShaderSourceProxy());
Proxy.Add(new ShaderHeaderProxy());
Proxy.Add(new ParticleEmitterProxy());
Proxy.Add(new ParticleEmitterFunctionProxy());
Proxy.Add(new ParticleSystemProxy());

View File

@@ -76,6 +76,13 @@ namespace FlaxEditor.Modules
{
}
/// <summary>
/// Called when Editor will leave the play mode.
/// </summary>
public virtual void OnPlayEnding()
{
}
/// <summary>
/// Called when Editor leaves the play mode.
/// </summary>

View File

@@ -711,7 +711,11 @@ namespace FlaxEditor.Modules
private void OnActorChildNodesDispose(ActorNode node)
{
if (Selection.Count == 0)
return;
// TODO: cache if selection contains any actor child node and skip this loop if no need to iterate
// TODO: or build a hash set with selected nodes for quick O(1) checks (cached until selection changes)
// Deselect child nodes
for (int i = 0; i < node.ChildNodes.Count; i++)

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
@@ -658,6 +659,48 @@ namespace FlaxEditor.Modules
//node?.TreeNode.OnActiveChanged();
}
private void OnActorDestroyChildren(Actor actor)
{
// Instead of doing OnActorParentChanged for every child lets remove all of them at once from that actor
ActorNode node = GetActorNode(actor);
if (node != null)
{
if (Editor.SceneEditing.HasSthSelected)
{
// Clear selection if one of the removed actors is selected
var selection = new HashSet<Actor>();
foreach (var e in Editor.SceneEditing.Selection)
{
if (e is ActorNode q && q.Actor)
selection.Add(q.Actor);
}
var count = actor.ChildrenCount;
for (int i = 0; i < count; i++)
{
var child = actor.GetChild(i);
if (selection.Contains(child))
{
Editor.SceneEditing.Deselect();
break;
}
}
}
// Remove all child nodes (upfront remove all nodes to run faster)
for (int i = 0; i < node.ChildNodes.Count; i++)
{
if (node.ChildNodes[i] is ActorNode child)
child.parentNode = null;
}
node.TreeNode.DisposeChildren();
for (int i = 0; i < node.ChildNodes.Count; i++)
{
node.ChildNodes[i].Dispose();
}
node.ChildNodes.Clear();
}
}
/// <summary>
/// Gets the actor node.
/// </summary>
@@ -709,6 +752,7 @@ namespace FlaxEditor.Modules
Level.ActorOrderInParentChanged += OnActorOrderInParentChanged;
Level.ActorNameChanged += OnActorNameChanged;
Level.ActorActiveChanged += OnActorActiveChanged;
Level.ActorDestroyChildren += OnActorDestroyChildren;
}
/// <inheritdoc />
@@ -726,6 +770,7 @@ namespace FlaxEditor.Modules
Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged;
Level.ActorNameChanged -= OnActorNameChanged;
Level.ActorActiveChanged -= OnActorActiveChanged;
Level.ActorDestroyChildren -= OnActorDestroyChildren;
// Cleanup graph
Root.Dispose();

View File

@@ -16,7 +16,6 @@ using FlaxEditor.Windows;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
using WindowDragHelper = FlaxEditor.GUI.Docking.WindowDragHelper;
using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel;
using FlaxEditor.Content.Settings;
using FlaxEditor.Options;
@@ -29,6 +28,40 @@ namespace FlaxEditor.Modules
/// <seealso cref="FlaxEditor.Modules.EditorModule" />
public sealed class UIModule : EditorModule
{
private class MainWindowDecorations : WindowDecorations
{
public MainWindowDecorations(RootControl window, bool iconOnly)
: base(window, iconOnly)
{
}
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
if (base.OnKeyDown(key))
return true;
// Fallback to the edit window for shortcuts
var editor = Editor.Instance;
return editor.Windows.EditWin.InputActions.Process(editor, this, key);
}
/// <inheritdoc />
public override void DrawBorders()
{
// Draw main window borders if using a custom style
var win = RootWindow.Window;
if (win.IsMaximized)
return;
var color = Editor.Instance.UI.StatusBar.StatusColor;
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
}
}
private Label _progressLabel;
private ProgressBar _progressBar;
private Button _outputLogButton;
@@ -97,6 +130,11 @@ namespace FlaxEditor.Modules
/// </summary>
public MainMenu MainMenu;
/// <summary>
/// The window decorations (title bar with buttons)
/// </summary>
public WindowDecorations WindowDecorations;
/// <summary>
/// The tool strip control.
/// </summary>
@@ -377,19 +415,11 @@ namespace FlaxEditor.Modules
InitToolstrip(mainWindow);
InitStatusBar(mainWindow);
InitDockPanel(mainWindow);
InitWindowDecorations(mainWindow);
Editor.Options.OptionsChanged += OnOptionsChanged;
// Add dummy control for drawing the main window borders if using a custom style
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
#endif
{
mainWindow.AddChild(new CustomWindowBorderControl
{
Size = Float2.Zero,
});
}
mainWindow.PerformLayout(true);
}
/// <inheritdoc />
@@ -410,23 +440,6 @@ namespace FlaxEditor.Modules
}
}
private class CustomWindowBorderControl : Control
{
/// <inheritdoc />
public override void Draw()
{
var win = RootWindow.Window;
if (win.IsMaximized)
return;
var color = Editor.Instance.UI.StatusBar.StatusColor;
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
}
}
/// <inheritdoc />
public override void OnEndInit()
{
@@ -511,10 +524,12 @@ namespace FlaxEditor.Modules
private void InitMainMenu(RootControl mainWindow)
{
MainMenu = new MainMenu(mainWindow)
MainMenu = new MainMenu()
{
Parent = mainWindow
};
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
MainMenu.Height = 28;
var inputOptions = Editor.Options.Options.Input;
@@ -654,6 +669,20 @@ namespace FlaxEditor.Modules
cm.AddButton("Information about Flax", () => new AboutDialog().Show());
}
private void InitWindowDecorations(RootControl mainWindow)
{
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
var driver = Platform.DisplayServer;
if (!string.IsNullOrEmpty(driver))
driver = $" ({driver})";
WindowDecorations = new MainWindowDecorations(mainWindow, !Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
{
Parent = mainWindow,
IconTooltipText = $"{mainWindow.RootWindow.Title}\nVersion {Globals.EngineVersion}\nConfiguration {configuration}\nGraphics {GPUDevice.Instance.RendererType}{driver}",
};
}
private void OnOptionsChanged(EditorOptions options)
{
var inputOptions = options.Input;
@@ -1063,6 +1092,7 @@ namespace FlaxEditor.Modules
{
// Clear UI references (GUI cannot be used after window closing)
MainMenu = null;
WindowDecorations = null;
ToolStrip = null;
MasterPanel = null;
StatusBar = null;

View File

@@ -10,7 +10,6 @@ using System.Text;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.GUI.Dialogs;
using FlaxEditor.GUI.Docking;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
using FlaxEditor.Windows.Profiler;
@@ -761,17 +760,16 @@ namespace FlaxEditor.Modules
settings.MinimumSize = new Float2(200, 150);
settings.StartPosition = WindowStartPosition.CenterScreen;
settings.ShowAfterFirstPaint = true;
#if PLATFORM_WINDOWS
if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
{
settings.HasBorder = false;
#if !PLATFORM_SDL
#if PLATFORM_WINDOWS && !PLATFORM_SDL
// Skip OS sizing frame and implement it using LeftButtonHit
settings.HasSizingFrame = false;
#endif
}
#elif PLATFORM_LINUX
#if PLATFORM_LINUX && !PLATFORM_SDL
settings.HasBorder = false;
#endif
MainWindow = Platform.CreateWindow(ref settings);
@@ -1226,6 +1224,13 @@ namespace FlaxEditor.Modules
Windows[i].OnPlayBegin();
}
/// <inheritdoc />
public override void OnPlayEnding()
{
for (int i = 0; i < Windows.Count; i++)
Windows[i].OnPlayEnding();
}
/// <inheritdoc />
public override void OnPlayEnd()
{

View File

@@ -139,6 +139,10 @@ namespace FlaxEditor.Options
[EditorDisplay("Common"), EditorOrder(240)]
public InputBinding ToggleFullscreen = new InputBinding(KeyboardKeys.F11);
[DefaultValue(typeof(InputBinding), "Ctrl+BackQuote")]
[EditorDisplay("Common"), EditorOrder(250)]
public InputBinding FocusConsoleCommand = new InputBinding(KeyboardKeys.BackQuote, KeyboardKeys.Control);
#endregion
#region File
@@ -647,5 +651,45 @@ namespace FlaxEditor.Options
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
#endregion
#region Node editors
[DefaultValue(typeof(InputBinding), "Shift+W")]
[EditorDisplay("Node editors"), EditorOrder(4500)]
public InputBinding NodesAlignTop = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Shift+A")]
[EditorDisplay("Node editors"), EditorOrder(4510)]
public InputBinding NodesAlignLeft = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Shift+S")]
[EditorDisplay("Node editors"), EditorOrder(4520)]
public InputBinding NodesAlignBottom = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Shift+D")]
[EditorDisplay("Node editors"), EditorOrder(4530)]
public InputBinding NodesAlignRight = new InputBinding(KeyboardKeys.D, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Alt+Shift+W")]
[EditorDisplay("Node editors"), EditorOrder(4540)]
public InputBinding NodesAlignMiddle = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift, KeyboardKeys.Alt);
[DefaultValue(typeof(InputBinding), "Alt+Shift+S")]
[EditorDisplay("Node editors"), EditorOrder(4550)]
public InputBinding NodesAlignCenter = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift, KeyboardKeys.Alt);
[DefaultValue(typeof(InputBinding), "Q")]
[EditorDisplay("Node editors"), EditorOrder(4560)]
public InputBinding NodesAutoFormat = new InputBinding(KeyboardKeys.Q);
[DefaultValue(typeof(InputBinding), "None")]
[EditorDisplay("Node editors"), EditorOrder(4570)]
public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.None);
[DefaultValue(typeof(InputBinding), "None")]
[EditorDisplay("Node editors"), EditorOrder(4580)]
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.None);
#endregion
}
}

View File

@@ -160,21 +160,47 @@ namespace FlaxEditor.Options
GameWindowThenRestore,
}
/// <summary>
/// Options for type of window decorations to use.
/// </summary>
public enum WindowDecorationsType
{
/// <summary>
/// Determined automatically based on the system and any known compatibility issues with native decorations.
/// </summary>
Auto,
/// <summary>
/// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window.
/// </summary>
[EditorDisplay(Name = "Auto (Child Only)")]
AutoChildOnly,
/// <summary>
/// Use native system window decorations on all windows.
/// </summary>
Native,
/// <summary>
/// Use custom client-side window decorations on all windows.
/// </summary>
[EditorDisplay(Name = "Client-side")]
ClientSide,
}
/// <summary>
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
/// </summary>
[DefaultValue(1.0f), Limit(0.1f, 10.0f)]
[EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")]
public float InterfaceScale { get; set; } = 1.0f;
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <summary>
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
/// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required.
/// </summary>
[DefaultValue(false)]
[EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar. Editor restart required.")]
public bool UseNativeWindowSystem { get; set; } = false;
#endif
[DefaultValue(WindowDecorationsType.AutoChildOnly)]
[EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar decorations. Editor restart required.")]
public WindowDecorationsType WindowDecorations { get; set; } = WindowDecorationsType.AutoChildOnly;
/// <summary>
/// Gets or sets a value indicating whether show selected camera preview in the editor window.

View File

@@ -6,6 +6,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Core/Math/Quaternion.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Engine/Serialization/JsonTools.h"
#include <ThirdParty/pugixml/pugixml.hpp>
@@ -327,6 +328,7 @@ ProjectInfo* ProjectInfo::Load(const String& path)
}
// Load
PROFILE_MEM(Editor);
auto project = New<ProjectInfo>();
if (project->LoadProject(path))
{

View File

@@ -76,9 +76,13 @@ namespace FlaxEditor.SceneGraph.Actors
// Skip removing this terrain file sif it's still referenced
var sceneReferences = Editor.GetAssetReferences(e.SceneId);
if (sceneReferences != null && sceneReferences.Contains(e.TerrainId))
{
Debug.Log($"Skip removing files used by terrain {e.TerrainId} on scene {e.SceneId} as it's still in use");
continue;
}
// Delete files
Debug.Log($"Removing files used by removed terrain {e.TerrainId} on scene {e.SceneId}");
foreach (var file in e.Files)
{
if (file != null && File.Exists(file))

View File

@@ -27,7 +27,7 @@ namespace FlaxEditor.SceneGraph
/// <summary>
/// The parent node.
/// </summary>
protected SceneGraphNode parentNode;
internal SceneGraphNode parentNode;
/// <summary>
/// Gets the children list.

View File

@@ -13,6 +13,7 @@
#include "Engine/Engine/EngineService.h"
#include "Engine/Platform/Thread.h"
#include "Engine/Threading/IRunnable.h"
#include "Engine/Profiler/ProfilerMemory.h"
void OnAsyncBegin(Thread* thread);
void OnAsyncEnd();
@@ -232,6 +233,8 @@ void OnAsyncEnd()
bool CodeEditingManagerService::Init()
{
PROFILE_MEM(Editor);
// Try get editors
#if USE_VISUAL_STUDIO_DTE
VisualStudioEditor::FindEditors(&CodeEditors);

View File

@@ -23,6 +23,7 @@
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Level.h"
#include "FlaxEngine.Gen.h"
@@ -77,7 +78,7 @@ namespace ScriptsBuilderImpl
void onScriptsReloadEnd();
void onScriptsLoaded();
void GetClassName(const StringAnsi& fullname, StringAnsi& className);
void GetClassName(const StringAnsiView fullname, StringAnsi& className);
void onCodeEditorAsyncOpenBegin()
{
@@ -276,7 +277,7 @@ bool ScriptsBuilder::GenerateProject(const StringView& customArgs)
return RunBuildTool(args);
}
void ScriptsBuilderImpl::GetClassName(const StringAnsi& fullname, StringAnsi& className)
void ScriptsBuilderImpl::GetClassName(const StringAnsiView fullname, StringAnsi& className)
{
const auto lastDotIndex = fullname.FindLast('.');
if (lastDotIndex != -1)
@@ -417,6 +418,7 @@ void ScriptsBuilder::GetBinariesConfiguration(const Char*& target, const Char*&
bool ScriptsBuilderImpl::compileGameScriptsAsyncInner()
{
PROFILE_MEM(Editor);
LOG(Info, "Starting scripts compilation...");
CallEvent(EventType::CompileStarted);
@@ -523,6 +525,8 @@ void ScriptsBuilderImpl::onEditorAssemblyUnloading(MAssembly* assembly)
bool ScriptsBuilderImpl::compileGameScriptsAsync()
{
PROFILE_MEM(Editor);
// Start
{
ScopeLock scopeLock(_locker);
@@ -566,6 +570,7 @@ bool ScriptsBuilderService::Init()
// Check flag
if (_isInited)
return false;
PROFILE_MEM(Editor);
_isInited = true;
// Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors
@@ -663,6 +668,9 @@ bool ScriptsBuilderService::Init()
void ScriptsBuilderService::Update()
{
PROFILE_CPU();
PROFILE_MEM(Editor);
// Send compilation events
{
ScopeLock scopeLock(_compileEventsLocker);

View File

@@ -19,6 +19,7 @@ namespace FlaxEditor.States
private readonly List<Guid> _scenesToLoad = new List<Guid>();
private readonly List<Scene> _scenesToUnload = new List<Scene>();
private Guid _lastSceneFromRequest;
private bool _sameSceneReload = false;
internal ChangingScenesState(Editor editor)
: base(editor)
@@ -164,10 +165,22 @@ namespace FlaxEditor.States
{
Assert.AreEqual(Guid.Empty, _lastSceneFromRequest, "Invalid state.");
// Bind events
Level.SceneLoaded += OnSceneEvent;
Level.SceneLoadError += OnSceneEvent;
Level.SceneUnloaded += OnSceneEvent;
// Bind events, only bind loading event and error if re-loading the same scene to avoid issues.
if (_scenesToUnload.Count == 1 && _scenesToLoad.Count == 1)
{
if (_scenesToLoad[0] == _scenesToUnload[0].ID)
{
Level.SceneLoaded += OnSceneEvent;
Level.SceneLoadError += OnSceneEvent;
_sameSceneReload = true;
}
}
if (!_sameSceneReload)
{
Level.SceneLoaded += OnSceneEvent;
Level.SceneLoadError += OnSceneEvent;
Level.SceneUnloaded += OnSceneEvent;
}
// Push scenes changing requests
for (int i = 0; i < _scenesToUnload.Count; i++)
@@ -210,9 +223,18 @@ namespace FlaxEditor.States
}
// Unbind events
Level.SceneLoaded -= OnSceneEvent;
Level.SceneLoadError -= OnSceneEvent;
Level.SceneUnloaded -= OnSceneEvent;
if (_sameSceneReload)
{
Level.SceneLoaded -= OnSceneEvent;
Level.SceneLoadError -= OnSceneEvent;
_sameSceneReload = false;
}
else
{
Level.SceneLoaded -= OnSceneEvent;
Level.SceneLoadError -= OnSceneEvent;
Level.SceneUnloaded -= OnSceneEvent;
}
}
private void OnSceneEvent(Scene scene, Guid sceneId)

View File

@@ -6,6 +6,7 @@ using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Surface.Archetypes
{
@@ -123,7 +124,8 @@ namespace FlaxEditor.Surface.Archetypes
case MaterialDomain.Particle:
case MaterialDomain.Deformable:
{
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit;
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit && info.ShadingModel != MaterialShadingModel.CustomLit;
bool isOpaque = info.BlendMode == MaterialBlendMode.Opaque;
bool withTess = info.TessellationMode != TessellationMethod.None;
GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit;
@@ -134,8 +136,8 @@ namespace FlaxEditor.Surface.Archetypes
GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit;
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || info.BlendMode != MaterialBlendMode.Opaque;
GetBox(MaterialNodeBoxes.Refraction).IsActive = info.BlendMode != MaterialBlendMode.Opaque;
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || !isOpaque;
GetBox(MaterialNodeBoxes.Refraction).IsActive = !isOpaque;
GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true;
GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess;
GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess;
@@ -260,6 +262,211 @@ namespace FlaxEditor.Surface.Archetypes
}
}
#if false // TODO: finish code editor based on RichTextBoxBase with text block parsing for custom styling
internal sealed class CustomCodeTextBox : RichTextBoxBase
{
protected override void OnParseTextBlocks()
{
base.OnParseTextBlocks();
// Single block for a whole text
// TODO: implement code parsing with HLSL syntax
var font = Style.Current.FontMedium;
var style = new TextBlockStyle
{
Font = new FontReference(font),
Color = Style.Current.Foreground,
BackgroundSelectedBrush = new SolidColorBrush(Style.Current.BackgroundSelected),
};
_textBlocks.Clear();
_textBlocks.Add(new TextBlock
{
Range = new TextRange
{
StartIndex = 0,
EndIndex = TextLength,
},
Style = style,
Bounds = new Rectangle(Float2.Zero, font.MeasureText(Text)),
});
}
#else
internal sealed class CustomCodeTextBox : TextBox
{
#endif
public override void Draw()
{
base.Draw();
// Draw border
if (!IsFocused)
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BorderNormal);
}
}
internal sealed class CustomCodeNode : SurfaceNode
{
private Rectangle _resizeButtonRect;
private Float2 _startResizingSize;
private Float2 _startResizingCornerOffset;
private bool _isResizing;
private CustomCodeTextBox _textBox;
private int SizeValueIndex => Archetype.TypeID == 8 ? 1 : 3; // Index of the Size stored in Values array
private Float2 SizeValue
{
get => (Float2)Values[SizeValueIndex];
set => SetValue(SizeValueIndex, value, false);
}
public CustomCodeNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
: base(id, context, nodeArch, groupArch)
{
Float2 pos = new Float2(FlaxEditor.Surface.Constants.NodeMarginX, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize), size;
if (nodeArch.TypeID == 8)
{
pos += new Float2(60, 0);
size = new Float2(172, 200);
}
else
{
pos += new Float2(0, 40);
size = new Float2(300, 200);
}
_textBox = new CustomCodeTextBox
{
IsMultiline = true,
Location = pos,
Size = size,
Parent = this,
AnchorMax = Float2.One,
};
_textBox.EditEnd += () => SetValue(0, _textBox.Text);
}
public override bool CanSelect(ref Float2 location)
{
return base.CanSelect(ref location) && !_resizeButtonRect.MakeOffsetted(Location).Contains(ref location);
}
public override void OnSurfaceLoaded(SurfaceNodeActions action)
{
base.OnSurfaceLoaded(action);
_textBox.Text = (string)Values[0];
var size = SizeValue;
if (Surface != null && Surface.GridSnappingEnabled)
size = Surface.SnapToGrid(size, true);
Resize(size.X, size.Y);
}
public override void OnValuesChanged()
{
base.OnValuesChanged();
var size = SizeValue;
Resize(size.X, size.Y);
_textBox.Text = (string)Values[0];
}
protected override void UpdateRectangles()
{
base.UpdateRectangles();
const float buttonMargin = FlaxEditor.Surface.Constants.NodeCloseButtonMargin;
const float buttonSize = FlaxEditor.Surface.Constants.NodeCloseButtonSize;
_resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin - 4, buttonSize, buttonSize);
}
public override void Draw()
{
base.Draw();
var style = Style.Current;
if (_isResizing)
{
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
}
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
}
public override void OnLostFocus()
{
if (_isResizing)
EndResizing();
base.OnLostFocus();
}
public override void OnEndMouseCapture()
{
if (_isResizing)
EndResizing();
base.OnEndMouseCapture();
}
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (base.OnMouseDown(location, button))
return true;
if (button == MouseButton.Left && _resizeButtonRect.Contains(ref location) && Surface.CanEdit)
{
// Start sliding
_isResizing = true;
_startResizingSize = Size;
_startResizingCornerOffset = Size - location;
StartMouseCapture();
Cursor = CursorType.SizeNWSE;
return true;
}
return false;
}
public override void OnMouseMove(Float2 location)
{
if (_isResizing)
{
var emptySize = CalculateNodeSize(0, 0);
var size = Float2.Max(location - emptySize + _startResizingCornerOffset, new Float2(240, 160));
Resize(size.X, size.Y);
}
else
{
base.OnMouseMove(location);
}
}
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _isResizing)
{
EndResizing();
return true;
}
return base.OnMouseUp(location, button);
}
private void EndResizing()
{
Cursor = CursorType.Default;
EndMouseCapture();
_isResizing = false;
if (_startResizingSize != Size)
{
var emptySize = CalculateNodeSize(0, 0);
SizeValue = Size - emptySize;
Surface.MarkAsEdited(false);
}
}
}
internal enum MaterialTemplateInputsMapping
{
/// <summary>
@@ -410,13 +617,15 @@ namespace FlaxEditor.Surface.Archetypes
new NodeArchetype
{
TypeID = 8,
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
Title = "Custom Code",
Description = "Custom HLSL shader code expression",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(300, 200),
DefaultValues = new object[]
{
"// Here you can add HLSL code\nOutput0 = Input0;"
"// Here you can add HLSL code\nOutput0 = Input0;",
new Float2(300, 200),
},
Elements = new[]
{
@@ -433,8 +642,6 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9),
NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10),
NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11),
NodeElementArchetype.Factory.TextBox(60, 0, 175, 200, 0),
}
},
new NodeArchetype
@@ -874,6 +1081,7 @@ namespace FlaxEditor.Surface.Archetypes
new NodeArchetype
{
TypeID = 38,
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
Title = "Custom Global Code",
Description = "Custom global HLSL shader code expression (placed before material shader code). Can contain includes to shader utilities or declare functions to reuse later.",
Flags = NodeFlags.MaterialGraph,
@@ -883,6 +1091,7 @@ namespace FlaxEditor.Surface.Archetypes
"// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}",
true,
(int)MaterialTemplateInputsMapping.Utilities,
new Float2(300, 240),
},
Elements = new[]
{
@@ -890,7 +1099,6 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
NodeElementArchetype.Factory.Text(0, 20, "Location"),
NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)),
NodeElementArchetype.Factory.TextBox(0, 40, 300, 200, 0),
}
},
new NodeArchetype

View File

@@ -459,7 +459,7 @@ namespace FlaxEditor.Surface.Archetypes
AlternativeTitles = new string[] { "Lightmap TexCoord" },
Description = "Lightmap UVs",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(110, 30),
Size = new Float2(110, 20),
Elements = new []
{
NodeElementArchetype.Factory.Output(0, "UVs", typeof(Float2), 0)
@@ -493,6 +493,19 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Bool(190, Surface.Constants.LayoutOffsetY * 4, 4),
}
},
new NodeArchetype
{
TypeID = 24,
Title = "Texture Size",
Description = "Gets the size of the texture (in pixels). If texture is during streaming, then returns size of the highest resident mip.",
Flags = NodeFlags.ParticleEmitterGraph,
Size = new Float2(160, 20),
Elements = new[]
{
NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0),
NodeElementArchetype.Factory.Output(0, "Size", typeof(Float3), 1),
}
},
};
}
}

View File

@@ -0,0 +1,43 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.Surface
{
/// <summary>
/// Node Alignment type
/// </summary>
[HideInEditor]
public enum NodeAlignmentType
{
/// <summary>
/// Align nodes vertically to top, matching top-most node
/// </summary>
Top,
/// <summary>
/// Align nodes vertically to middle, using average of all nodes
/// </summary>
Middle,
/// <summary>
/// Align nodes vertically to bottom, matching bottom-most node
/// </summary>
Bottom,
/// <summary>
/// Align nodes horizontally to left, matching left-most node
/// </summary>
Left,
/// <summary>
/// Align nodes horizontally to center, using average of all nodes
/// </summary>
Center,
/// <summary>
/// Align nodes horizontally to right, matching right-most node
/// </summary>
Right,
}
}

View File

@@ -40,6 +40,11 @@ namespace FlaxEditor.Surface
[HideInEditor]
public class SurfaceNode : SurfaceControl
{
/// <summary>
/// The box to draw a highlight around. Drawing will be skipped if null.
/// </summary>
internal Box highlightBox;
/// <summary>
/// Flag used to discard node values setting during event sending for node UI flushing.
/// </summary>
@@ -912,7 +917,7 @@ namespace FlaxEditor.Surface
/// <inheritdoc />
public override bool OnTestTooltipOverControl(ref Float2 location)
{
return _headerRect.Contains(ref location) && ShowTooltip;
return _headerRect.Contains(ref location) && ShowTooltip && !Surface.IsConnecting && !Surface.IsBoxSelecting;
}
/// <inheritdoc />
@@ -1070,7 +1075,7 @@ namespace FlaxEditor.Surface
// Header
var headerColor = style.BackgroundHighlighted;
if (_headerRect.Contains(ref _mousePosition))
if (_headerRect.Contains(ref _mousePosition) && !Surface.IsConnecting && !Surface.IsBoxSelecting)
headerColor *= 1.07f;
Render2D.FillRectangle(_headerRect, headerColor);
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
@@ -1078,7 +1083,8 @@ namespace FlaxEditor.Surface
// Close button
if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit)
{
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey);
bool highlightClose = _closeButtonRect.Contains(_mousePosition) && !Surface.IsConnecting && !Surface.IsBoxSelecting;
Render2D.DrawSprite(style.Cross, _closeButtonRect, highlightClose ? style.Foreground : style.ForegroundGrey);
}
// Footer
@@ -1101,6 +1107,9 @@ namespace FlaxEditor.Surface
Render2D.DrawSprite(icon, new Rectangle(-7, -7, 16, 16), new Color(0.9f, 0.9f, 0.9f));
Render2D.DrawSprite(icon, new Rectangle(-6, -6, 14, 14), new Color(0.894117647f, 0.0784313725f, 0.0f));
}
if (highlightBox != null)
Render2D.DrawRectangle(highlightBox.Bounds, style.BorderHighlighted, 2f);
}
/// <inheritdoc />
@@ -1123,8 +1132,9 @@ namespace FlaxEditor.Surface
if (base.OnMouseUp(location, button))
return true;
// Close
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
// Close/ delete
bool canDelete = !Surface.IsConnecting && !Surface.WasBoxSelecting && !Surface.WasMovingSelection;
if (button == MouseButton.Left && canDelete && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
{
Surface.Delete(this);
return true;

View File

@@ -191,7 +191,16 @@ namespace FlaxEditor.Surface
private ContextMenuButton _cmCopyButton;
private ContextMenuButton _cmDuplicateButton;
private ContextMenuChildMenu _cmFormatNodesMenu;
private ContextMenuButton _cmFormatNodesConnectionButton;
private ContextMenuButton _cmAlignNodesTopButton;
private ContextMenuButton _cmAlignNodesMiddleButton;
private ContextMenuButton _cmAlignNodesBottomButton;
private ContextMenuButton _cmAlignNodesLeftButton;
private ContextMenuButton _cmAlignNodesCenterButton;
private ContextMenuButton _cmAlignNodesRightButton;
private ContextMenuButton _cmDistributeNodesHorizontallyButton;
private ContextMenuButton _cmDistributeNodesVerticallyButton;
private ContextMenuButton _cmRemoveNodeConnectionsButton;
private ContextMenuButton _cmRemoveBoxConnectionsButton;
private readonly Float2 ContextMenuOffset = new Float2(5);
@@ -399,10 +408,26 @@ namespace FlaxEditor.Surface
}
menu.AddSeparator();
_cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () => { FormatGraph(SelectedNodes); });
_cmFormatNodesConnectionButton.Enabled = CanEdit && HasNodesSelection;
_cmFormatNodesMenu = menu.AddChildMenu("Format node(s)");
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection;
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () =>
_cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); });
_cmFormatNodesMenu.ContextMenu.AddSeparator();
_cmAlignNodesTopButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align top", Editor.Instance.Options.Options.Input.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); });
_cmAlignNodesMiddleButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align middle", Editor.Instance.Options.Options.Input.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); });
_cmAlignNodesBottomButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align bottom", Editor.Instance.Options.Options.Input.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); });
_cmFormatNodesMenu.ContextMenu.AddSeparator();
_cmAlignNodesLeftButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align left", Editor.Instance.Options.Options.Input.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); });
_cmAlignNodesCenterButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align center", Editor.Instance.Options.Options.Input.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); });
_cmAlignNodesRightButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align right", Editor.Instance.Options.Options.Input.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); });
_cmFormatNodesMenu.ContextMenu.AddSeparator();
_cmDistributeNodesHorizontallyButton = _cmFormatNodesMenu.ContextMenu.AddButton("Distribute horizontally", Editor.Instance.Options.Options.Input.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); });
_cmDistributeNodesVerticallyButton = _cmFormatNodesMenu.ContextMenu.AddButton("Distribute vertically", Editor.Instance.Options.Options.Input.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); });
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections", () =>
{
var nodes = ((List<SurfaceNode>)menu.Tag);
@@ -428,8 +453,10 @@ namespace FlaxEditor.Surface
MarkAsEdited();
});
_cmRemoveNodeConnectionsButton.Enabled = CanEdit;
_cmRemoveBoxConnectionsButton = menu.AddButton("Remove all connections to that box", () =>
bool anyConnection = SelectedNodes.Any(n => n.GetBoxes().Any(b => b.HasAnyConnection));
_cmRemoveNodeConnectionsButton.Enabled = CanEdit && anyConnection;
_cmRemoveBoxConnectionsButton = menu.AddButton("Remove all socket connections", () =>
{
var boxUnderMouse = (Box)_cmRemoveBoxConnectionsButton.Tag;
if (Undo != null)
@@ -450,6 +477,16 @@ namespace FlaxEditor.Surface
var boxUnderMouse = GetChildAtRecursive(location) as Box;
_cmRemoveBoxConnectionsButton.Enabled = boxUnderMouse != null && boxUnderMouse.HasAnyConnection;
_cmRemoveBoxConnectionsButton.Tag = boxUnderMouse;
if (boxUnderMouse != null)
{
boxUnderMouse.ParentNode.highlightBox = boxUnderMouse;
menu.VisibleChanged += (c) =>
{
if (!c.Visible)
boxUnderMouse.ParentNode.highlightBox = null;
};
}
}
controlUnderMouse?.OnShowSecondaryContextMenu(menu, controlUnderMouse.PointFromParent(location));

View File

@@ -225,7 +225,11 @@ namespace FlaxEditor.Surface
_rootControl.DrawComments();
if (IsSelecting)
// Reset input flags here because this is the closest to Update we have
WasBoxSelecting = IsBoxSelecting;
WasMovingSelection = IsMovingSelection;
if (IsBoxSelecting)
{
DrawSelection();
}

View File

@@ -282,5 +282,122 @@ namespace FlaxEditor.Surface
return maxOffset;
}
/// <summary>
/// Align given nodes on a graph using the given alignment type.
/// Ignores any potential overlap.
/// </summary>
/// <param name="nodes">List of nodes</param>
/// <param name="alignmentType">Alignemnt type</param>
public void AlignNodes(List<SurfaceNode> nodes, NodeAlignmentType alignmentType)
{
if(nodes.Count <= 1)
return;
var undoActions = new List<MoveNodesAction>();
var boundingBox = GetNodesBounds(nodes);
for(int i = 0; i < nodes.Count; i++)
{
var centerY = boundingBox.Center.Y - (nodes[i].Height / 2);
var centerX = boundingBox.Center.X - (nodes[i].Width / 2);
var newLocation = alignmentType switch
{
NodeAlignmentType.Top => new Float2(nodes[i].Location.X, boundingBox.Top),
NodeAlignmentType.Middle => new Float2(nodes[i].Location.X, centerY),
NodeAlignmentType.Bottom => new Float2(nodes[i].Location.X, boundingBox.Bottom - nodes[i].Height),
NodeAlignmentType.Left => new Float2(boundingBox.Left, nodes[i].Location.Y),
NodeAlignmentType.Center => new Float2(centerX, nodes[i].Location.Y),
NodeAlignmentType.Right => new Float2(boundingBox.Right - nodes[i].Width, nodes[i].Location.Y),
_ => throw new NotImplementedException($"Unsupported node alignment type: {alignmentType}"),
};
var locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
if(Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
MarkAsEdited(false);
Undo?.AddAction(new MultiUndoAction(undoActions, $"Align nodes ({alignmentType})"));
}
/// <summary>
/// Distribute the given nodes as equally as possible inside the bounding box, if no fit can be done it will use a default pad of 10 pixels between nodes.
/// </summary>
/// <param name="nodes">List of nodes</param>
/// <param name="vertically">If false will be done horizontally, if true will be done vertically</param>
public void DistributeNodes(List<SurfaceNode> nodes, bool vertically)
{
if(nodes.Count <= 1)
return;
var undoActions = new List<MoveNodesAction>();
var boundingBox = GetNodesBounds(nodes);
float padding = 10;
float totalSize = 0;
for (int i = 0; i < nodes.Count; i++)
{
if (vertically)
{
totalSize += nodes[i].Height;
}
else
{
totalSize += nodes[i].Width;
}
}
if(vertically)
{
nodes.Sort((leftValue, rightValue) => { return leftValue.Y.CompareTo(rightValue.Y); });
float position = boundingBox.Top;
if(totalSize < boundingBox.Height)
{
padding = (boundingBox.Height - totalSize) / nodes.Count;
}
for(int i = 0; i < nodes.Count; i++)
{
var newLocation = new Float2(nodes[i].X, position);
var locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
position += nodes[i].Height + padding;
if (Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
}
else
{
nodes.Sort((leftValue, rightValue) => { return leftValue.X.CompareTo(rightValue.X); });
float position = boundingBox.Left;
if(totalSize < boundingBox.Width)
{
padding = (boundingBox.Width - totalSize) / nodes.Count;
}
for(int i = 0; i < nodes.Count; i++)
{
var newLocation = new Float2(position, nodes[i].Y);
var locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
position += nodes[i].Width + padding;
if (Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
}
MarkAsEdited(false);
Undo?.AddAction(new MultiUndoAction(undoActions, vertically ? "Distribute nodes vertically" : "Distribute nodes horizontally"));
}
}
}

View File

@@ -27,6 +27,7 @@ namespace FlaxEditor.Surface
private Float2 _movingNodesDelta;
private Float2 _gridRoundingDelta;
private HashSet<SurfaceNode> _movingNodes;
private HashSet<SurfaceNode> _temporarySelectedNodes;
private readonly Stack<InputBracket> _inputBrackets = new Stack<InputBracket>();
private class InputBracket
@@ -130,13 +131,34 @@ namespace FlaxEditor.Surface
if (_rootControl.Children[i] is SurfaceControl control)
{
var select = control.IsSelectionIntersecting(ref selectionRect);
if (select != control.IsSelected)
if (Root.GetKey(KeyboardKeys.Shift))
{
control.IsSelected = select;
selectionChanged = true;
if (select == control.IsSelected && _temporarySelectedNodes.Contains(control))
{
control.IsSelected = !select;
selectionChanged = true;
}
}
else if (Root.GetKey(KeyboardKeys.Control))
{
if (select != control.IsSelected && !_temporarySelectedNodes.Contains(control))
{
control.IsSelected = select;
selectionChanged = true;
}
}
else
{
if (select != control.IsSelected)
{
control.IsSelected = select;
selectionChanged = true;
}
}
}
}
if (selectionChanged)
SelectionChanged?.Invoke();
}
@@ -461,6 +483,19 @@ namespace FlaxEditor.Surface
// Cache data
_isMovingSelection = false;
_mousePos = location;
if(_temporarySelectedNodes == null)
_temporarySelectedNodes = new HashSet<SurfaceNode>();
else
_temporarySelectedNodes.Clear();
for (int i = 0; i < _rootControl.Children.Count; i++)
{
if (_rootControl.Children[i] is SurfaceNode node && node.IsSelected)
{
_temporarySelectedNodes.Add(node);
}
}
if (button == MouseButton.Left)
{
_leftMouseDown = true;
@@ -488,9 +523,11 @@ namespace FlaxEditor.Surface
// Check if user is pressing control
if (Root.GetKey(KeyboardKeys.Control))
{
// Add/remove from selection
controlUnderMouse.IsSelected = !controlUnderMouse.IsSelected;
SelectionChanged?.Invoke();
AddToSelection(controlUnderMouse);
}
else if (Root.GetKey(KeyboardKeys.Shift))
{
RemoveFromSelection(controlUnderMouse);
}
// Check if node isn't selected
else if (!controlUnderMouse.IsSelected)
@@ -500,10 +537,14 @@ namespace FlaxEditor.Surface
}
// Start moving selected nodes
StartMouseCapture();
_movingSelectionViewPos = _rootControl.Location;
_movingNodesDelta = Float2.Zero;
OnGetNodesToMove();
if (!Root.GetKey(KeyboardKeys.Shift))
{
StartMouseCapture();
_movingSelectionViewPos = _rootControl.Location;
_movingNodesDelta = Float2.Zero;
OnGetNodesToMove();
}
Focus();
return true;
}
@@ -515,7 +556,12 @@ namespace FlaxEditor.Surface
{
// Start selecting or commenting
StartMouseCapture();
ClearSelection();
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift))
{
ClearSelection();
}
Focus();
return true;
}

View File

@@ -232,15 +232,25 @@ namespace FlaxEditor.Surface
}
/// <summary>
/// Gets a value indicating whether user is selecting nodes.
/// Gets a value indicating whether user is box selecting nodes.
/// </summary>
public bool IsSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator == null;
public bool IsBoxSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator == null;
/// <summary>
/// Gets a value indicating whether user was previously box selecting nodes.
/// </summary>
public bool WasBoxSelecting { get; private set; }
/// <summary>
/// Gets a value indicating whether user is moving selected nodes.
/// </summary>
public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigator == null;
/// <summary>
/// Gets a value indicating whether user was previously moving selected nodes.
/// </summary>
public bool WasMovingSelection { get; private set; }
/// <summary>
/// Gets a value indicating whether user is connecting nodes.
/// </summary>
@@ -405,6 +415,15 @@ namespace FlaxEditor.Surface
new InputActionsContainer.Binding(options => options.Paste, Paste),
new InputActionsContainer.Binding(options => options.Cut, Cut),
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
new InputActionsContainer.Binding(options => options.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }),
new InputActionsContainer.Binding(options => options.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); }),
new InputActionsContainer.Binding(options => options.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); }),
new InputActionsContainer.Binding(options => options.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); }),
new InputActionsContainer.Binding(options => options.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); }),
new InputActionsContainer.Binding(options => options.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); }),
new InputActionsContainer.Binding(options => options.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); }),
new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }),
new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }),
});
Context.ControlSpawned += OnSurfaceControlSpawned;
@@ -710,6 +729,18 @@ namespace FlaxEditor.Surface
SelectionChanged?.Invoke();
}
/// <summary>
/// Removes the specified control from the selection.
/// </summary>
/// <param name="control">The control.</param>
public void RemoveFromSelection(SurfaceControl control)
{
if (!control.IsSelected)
return;
control.IsSelected = false;
SelectionChanged?.Invoke();
}
/// <summary>
/// Selects the specified control.
/// </summary>

View File

@@ -1518,6 +1518,7 @@ namespace FlaxEditor.Utilities
inputActions.Add(options => options.OpenScriptsProject, () => Editor.Instance.CodeEditing.OpenSolution());
inputActions.Add(options => options.GenerateScriptsProject, () => Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync());
inputActions.Add(options => options.RecompileScripts, ScriptsBuilder.Compile);
inputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
}
internal static string ToPathProject(string path)
@@ -1558,5 +1559,17 @@ namespace FlaxEditor.Utilities
c = c.Parent;
return c as ISceneEditingContext;
}
internal static bool UseCustomWindowDecorations(bool isMainWindow = false)
{
return Editor.Instance.Options.Options.Interface.WindowDecorations switch
{
Options.InterfaceOptions.WindowDecorationsType.Auto => !Platform.SupportsNativeDecorations,
Options.InterfaceOptions.WindowDecorationsType.AutoChildOnly => !isMainWindow ? !Platform.SupportsNativeDecorations : true,
Options.InterfaceOptions.WindowDecorationsType.Native => false,
Options.InterfaceOptions.WindowDecorationsType.ClientSide => true,
_ => throw new ArgumentOutOfRangeException()
};
}
}
}

View File

@@ -5,6 +5,7 @@
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/MaterialInstance.h"
#include "Engine/Content/Content.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Level.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Actors/PointLight.h"
@@ -263,6 +264,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
bool ViewportIconsRendererService::Init()
{
PROFILE_MEM(Editor);
QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
#define INIT(type, path) \
InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \

View File

@@ -129,6 +129,9 @@ namespace FlaxEditor.Windows
"Mono Project - www.mono-project.com",
#if USE_NETCORE
".NET - www.dotnet.microsoft.com",
#endif
#if PLATFORM_SDL
"Simple DirectMedia Layer - www.libsdl.org",
#endif
"FreeType Project - www.freetype.org",
"Assimp - www.assimp.sourceforge.net",

View File

@@ -236,6 +236,7 @@ namespace FlaxEditor.Windows.Assets
var group = layout.Group("General");
var minScreenSize = group.FloatValue("Min Screen Size", "The minimum screen size to draw model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.");
minScreenSize.ValueBox.SlideSpeed = 0.005f;
minScreenSize.ValueBox.MinValue = 0.0f;
minScreenSize.ValueBox.MaxValue = 1.0f;
minScreenSize.ValueBox.Value = proxy.Asset.MinScreenSize;
@@ -476,12 +477,12 @@ namespace FlaxEditor.Windows.Assets
}
}
[EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs), VisibleIf("ShowUVs")]
[Tooltip("Level Of Detail index to preview UVs layout.")]
[EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs, 0.01f), VisibleIf("ShowUVs")]
[Tooltip("Level Of Detail index to preview UVs layout at.")]
public int LOD = 0;
[EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000), VisibleIf("ShowUVs")]
[Tooltip("Mesh index to preview UVs layout. Use -1 for all meshes")]
[EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000, 0.01f), VisibleIf("ShowUVs")]
[Tooltip("Mesh index to show UVs layout for. Use -1 to display all UVs of all meshes")]
public int Mesh = -1;
private bool ShowUVs => _uvChannel != UVChannel.None;

View File

@@ -81,6 +81,7 @@ namespace FlaxEditor.Windows.Assets
}
var resolution = group.FloatValue("Resolution Scale", Window.Editor.CodeDocs.GetTooltip(typeof(ModelTool.Options), nameof(ModelImportSettings.Settings.SDFResolution)));
resolution.ValueBox.SlideSpeed = 0.001f;
resolution.ValueBox.MinValue = 0.0001f;
resolution.ValueBox.MaxValue = 100.0f;
resolution.ValueBox.Value = sdf.Texture != null ? sdf.ResolutionScale : 1.0f;

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Content;
using FlaxEditor.CustomEditors;
using FlaxEditor.GUI;
using FlaxEditor.Scripting;
using FlaxEditor.Surface;
using FlaxEditor.Viewport.Previews;
@@ -114,6 +115,7 @@ namespace FlaxEditor.Windows.Assets
private readonly PropertiesProxy _properties;
private Tab _previewTab;
private ToolStripButton _showSourceCodeButton;
/// <inheritdoc />
public ParticleEmitterWindow(Editor editor, AssetItem item)
@@ -146,7 +148,8 @@ namespace FlaxEditor.Windows.Assets
// Toolstrip
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
_toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code");
_showSourceCodeButton = _toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode);
_showSourceCodeButton.LinkTooltip("Show generated shader source code");
_toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
}
@@ -285,5 +288,15 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
public SearchAssetTypes AssetType => SearchAssetTypes.ParticleEmitter;
/// <inheritdoc />
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (_asset == null)
return;
_showSourceCodeButton.Enabled = _asset.HasShaderCode;
}
}
}

View File

@@ -97,10 +97,7 @@ namespace FlaxEditor.Windows.Assets
// For single node selected scroll view so user can see it
if (nodes.Count == 1)
{
nodes[0].ExpandAllParents(true);
ScrollViewTo(nodes[0]);
}
ScrollToSelectedNode();
}
// Update properties editor

View File

@@ -318,7 +318,7 @@ namespace FlaxEditor.Windows
private Color _colorWarning;
private Color _colorError;
private bool _colorDebugLogText;
/// <summary>
/// Initializes a new instance of the <see cref="DebugLogWindow"/> class.
/// </summary>
@@ -352,24 +352,12 @@ namespace FlaxEditor.Windows
editor.Options.Apply(editor.Options.Options);
}).SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
toolstrip.AddSeparator();
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () =>
{
UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked);
editor.Options.Options.Interface.DebugLogShowErrorMessages = _groupButtons[0].Checked;
editor.Options.Apply(editor.Options.Options);
}).SetAutoCheck(true).LinkTooltip("Shows/hides error messages");
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () =>
{
UpdateLogTypeVisibility(LogGroup.Warning, _groupButtons[1].Checked);
editor.Options.Options.Interface.DebugLogShowWarningMessages = _groupButtons[1].Checked;
editor.Options.Apply(editor.Options.Options);
}).SetAutoCheck(true).LinkTooltip("Shows/hides warning messages");
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () =>
{
UpdateLogTypeVisibility(LogGroup.Info, _groupButtons[2].Checked);
editor.Options.Options.Interface.DebugLogShowInfoMessages = _groupButtons[2].Checked;
editor.Options.Apply(editor.Options.Options);
}).SetAutoCheck(true).LinkTooltip("Shows/hides info messages");
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => { OnGroupButtonPressed(0); })
.SetAutoCheck(true).LinkTooltip("Shows/hides error messages");
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () => { OnGroupButtonPressed(1); })
.SetAutoCheck(true).LinkTooltip("Shows/hides warning messages");
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () => { OnGroupButtonPressed(2); })
.SetAutoCheck(true).LinkTooltip("Shows/hides info messages");
UpdateCount();
// Split panel
@@ -418,6 +406,27 @@ namespace FlaxEditor.Windows
OnEditorOptionsChanged(Editor.Options.Options);
}
private void OnGroupButtonPressed(int index)
{
UpdateLogTypeVisibility((LogGroup)index, _groupButtons[index].Checked);
if (Input.GetKey(KeyboardKeys.Shift))
{
for (int i = 0; i < (int)LogGroup.Max; i++)
{
if (i == index)
continue;
_groupButtons[i].Checked = !_groupButtons[index].Checked;
UpdateLogTypeVisibility((LogGroup)i, _groupButtons[i].Checked);
}
}
var options = Editor.Options.Options.Interface;
options.DebugLogShowErrorMessages = _groupButtons[0].Checked;
options.DebugLogShowWarningMessages = _groupButtons[1].Checked;
options.DebugLogShowInfoMessages = _groupButtons[2].Checked;
Editor.Options.Apply(Editor.Options.Options);
}
private void OnEditorOptionsChanged(EditorOptions options)
{
_timestampsFormats = options.Interface.DebugLogTimestampsFormat;
@@ -438,6 +447,10 @@ namespace FlaxEditor.Windows
/// </summary>
public void Clear()
{
lock (_locker)
{
_pendingEntries.Clear();
}
if (_entriesPanel == null)
return;
RemoveEntries();
@@ -455,15 +468,9 @@ namespace FlaxEditor.Windows
// Create new entry
switch (_timestampsFormats)
{
case InterfaceOptions.TimestampsFormats.Utc:
desc.Title = $"[{DateTime.UtcNow}] {desc.Title}";
break;
case InterfaceOptions.TimestampsFormats.LocalTime:
desc.Title = $"[{DateTime.Now}] {desc.Title}";
break;
case InterfaceOptions.TimestampsFormats.TimeSinceStartup:
desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title;
break;
case InterfaceOptions.TimestampsFormats.Utc: desc.Title = $"[{DateTime.UtcNow}] {desc.Title}"; break;
case InterfaceOptions.TimestampsFormats.LocalTime: desc.Title = $"[{DateTime.Now}] {desc.Title}"; break;
case InterfaceOptions.TimestampsFormats.TimeSinceStartup: desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title; break;
}
var newEntry = new LogEntry(this, ref desc);
@@ -732,10 +739,10 @@ namespace FlaxEditor.Windows
}
/// <inheritdoc />
public override void OnPlayBegin()
public override void OnPlayBeginning()
{
// Clear on Play
if (_clearOnPlayButton.Checked)
if (Editor.Options.Options.Interface.DebugLogClearOnPlay)
{
Clear();
}

View File

@@ -219,6 +219,13 @@ namespace FlaxEditor.Windows
{
}
/// <summary>
/// Called when Editor will leave the play mode.
/// </summary>
public virtual void OnPlayEnding()
{
}
/// <summary>
/// Called when Editor leaves the play mode.
/// </summary>

View File

@@ -405,6 +405,7 @@ namespace FlaxEditor.Windows
return;
Editor.Instance.SceneEditing.Delete();
});
InputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
}
private void ChangeViewportRatio(ViewportScaleOptions v)

View File

@@ -830,6 +830,15 @@ namespace FlaxEditor.Windows
OnOutputTextChanged();
}
/// <summary>
/// Focus the debug command line and ensure that the output log window is visible.
/// </summary>
public void FocusCommand()
{
FocusOrShow();
_commandLineBox.Focus();
}
/// <inheritdoc />
public override void Update(float deltaTime)
{

View File

@@ -34,6 +34,7 @@ namespace FlaxEditor.Windows.Profiler
private List<ClickableRow> _tableRowsCache;
private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder;
private Asset[] _assetsCache;
public Assets()
: base("Assets")
@@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler
_stringBuilder = new StringBuilder();
// Capture current assets usage info
var assets = FlaxEngine.Content.Assets;
var resources = new Resource[assets.Length];
FlaxEngine.Content.GetAssets(ref _assetsCache, out var count);
var resources = new Resource[count];
ulong totalMemoryUsage = 0;
for (int i = 0; i < resources.Length; i++)
for (int i = 0; i < count; i++)
{
var asset = assets[i];
var asset = _assetsCache[i];
ref var resource = ref resources[i];
if (!asset)
continue;
@@ -179,6 +180,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null)
_resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources);
Array.Clear(_assetsCache);
}
/// <inheritdoc />
@@ -200,6 +202,7 @@ namespace FlaxEditor.Windows.Profiler
_resourceCache?.Clear();
_tableRowsCache?.Clear();
_stringBuilder?.Clear();
_assetsCache = null;
base.OnDestroy();
}

View File

@@ -2,6 +2,8 @@
#if USE_PROFILER
using System;
using System.Collections.Generic;
using FlaxEditor.GUI;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -13,9 +15,22 @@ namespace FlaxEditor.Windows.Profiler
/// <seealso cref="FlaxEditor.Windows.Profiler.ProfilerMode" />
internal sealed class Memory : ProfilerMode
{
private struct FrameData
{
public ProfilerMemory.GroupsArray Usage;
public ProfilerMemory.GroupsArray Peek;
public ProfilerMemory.GroupsArray Count;
}
private readonly SingleChart _nativeAllocationsChart;
private readonly SingleChart _managedAllocationsChart;
private readonly Table _table;
private SamplesBuffer<FrameData> _frames;
private List<Row> _tableRowsCache;
private string[] _groupNames;
private int[] _groupOrder;
private Label _warningText;
public Memory()
: base("Memory")
{
@@ -50,6 +65,70 @@ namespace FlaxEditor.Windows.Profiler
Parent = layout,
};
_managedAllocationsChart.SelectedSampleChanged += OnSelectedSampleChanged;
// Warning text
if (!ProfilerMemory.Enabled)
{
_warningText = new Label
{
Text = "Detailed memory profiling is disabled. Run with command line '-mem'",
TextColor = Color.Red,
Visible = false,
Parent = layout,
};
}
// Table
var style = Style.Current;
var headerColor = style.LightBackground;
var textColor = style.Foreground;
_table = new Table
{
Columns = new[]
{
new ColumnDefinition
{
UseExpandCollapseMode = true,
CellAlignment = TextAlignment.Near,
Title = "Group",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Usage",
TitleBackgroundColor = headerColor,
FormatValue = FormatCellBytes,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Peek",
TitleBackgroundColor = headerColor,
FormatValue = FormatCellBytes,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Count",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
},
Parent = layout,
};
_table.Splits = new[]
{
0.5f,
0.2f,
0.2f,
0.1f,
};
}
private string FormatCellBytes(object x)
{
return Utilities.Utils.FormatBytesCount(Convert.ToUInt64(x));
}
/// <inheritdoc />
@@ -57,6 +136,7 @@ namespace FlaxEditor.Windows.Profiler
{
_nativeAllocationsChart.Clear();
_managedAllocationsChart.Clear();
_frames?.Clear();
}
/// <inheritdoc />
@@ -84,6 +164,19 @@ namespace FlaxEditor.Windows.Profiler
_nativeAllocationsChart.AddSample(nativeMemoryAllocation);
_managedAllocationsChart.AddSample(managedMemoryAllocation);
// Gather memory profiler stats for groups
var frame = new FrameData
{
Usage = ProfilerMemory.GetGroups(0),
Peek = ProfilerMemory.GetGroups(1),
Count = ProfilerMemory.GetGroups(2),
};
if (_frames == null)
_frames = new SamplesBuffer<FrameData>();
if (_groupNames == null)
_groupNames = ProfilerMemory.GetGroupNames();
_frames.Add(frame);
}
/// <inheritdoc />
@@ -91,6 +184,112 @@ namespace FlaxEditor.Windows.Profiler
{
_nativeAllocationsChart.SelectedSampleIndex = selectedFrame;
_managedAllocationsChart.SelectedSampleIndex = selectedFrame;
UpdateTable(selectedFrame);
}
/// <inheritdoc />
public override void OnDestroy()
{
_tableRowsCache?.Clear();
_groupNames = null;
_groupOrder = null;
base.OnDestroy();
}
private void UpdateTable(int selectedFrame)
{
if (_frames == null)
return;
if (_tableRowsCache == null)
_tableRowsCache = new List<Row>();
_table.IsLayoutLocked = true;
RecycleTableRows(_table, _tableRowsCache);
UpdateTableInner(selectedFrame);
_table.UnlockChildrenRecursive();
_table.PerformLayout();
}
private unsafe void UpdateTableInner(int selectedFrame)
{
if (_frames.Count == 0)
return;
if (_warningText != null)
_warningText.Visible = true;
var frame = _frames.Get(selectedFrame);
var totalUage = frame.Usage.Values0[(int)ProfilerMemory.Groups.TotalTracked];
var totalPeek = frame.Peek.Values0[(int)ProfilerMemory.Groups.TotalTracked];
var totalCount = frame.Count.Values0[(int)ProfilerMemory.Groups.TotalTracked];
// Sort by memory size
if (_groupOrder == null)
_groupOrder = new int[(int)ProfilerMemory.Groups.MAX];
for (int i = 0; i < (int)ProfilerMemory.Groups.MAX; i++)
_groupOrder[i] = i;
Array.Sort(_groupOrder, (x, y) =>
{
var tmp = _frames.Get(selectedFrame);
return tmp.Usage.Values0[y].CompareTo(tmp.Usage.Values0[x]);
});
// Add rows
var rowColor2 = Style.Current.Background * 1.4f;
for (int i = 0; i < (int)ProfilerMemory.Groups.MAX; i++)
{
var group = _groupOrder[i];
var groupUsage = frame.Usage.Values0[group];
if (groupUsage <= 0)
continue;
var groupPeek = frame.Peek.Values0[group];
var groupCount = frame.Count.Values0[group];
Row row;
if (_tableRowsCache.Count != 0)
{
var last = _tableRowsCache.Count - 1;
row = _tableRowsCache[last];
_tableRowsCache.RemoveAt(last);
}
else
{
row = new Row
{
Values = new object[4],
BackgroundColors = new Color[4],
};
}
{
// Group
row.Values[0] = _groupNames[group];
// Usage
row.Values[1] = groupUsage;
row.BackgroundColors[1] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupUsage / totalUage) * 0.5f);
// Peek
row.Values[2] = groupPeek;
row.BackgroundColors[2] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupPeek / totalPeek) * 0.5f);
// Count
row.Values[3] = groupCount;
row.BackgroundColors[3] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupCount / totalCount) * 0.5f);
}
row.Width = _table.Width;
row.BackgroundColor = i % 2 == 1 ? rowColor2 : Color.Transparent;
row.Parent = _table;
var useBackground = group != (int)ProfilerMemory.Groups.Total &&
group != (int)ProfilerMemory.Groups.TotalTracked &&
group != (int)ProfilerMemory.Groups.Malloc;
if (!useBackground)
{
for (int k = 1; k < row.BackgroundColors.Length; k++)
row.BackgroundColors[k] = Color.Transparent;
}
}
}
}
}

View File

@@ -35,6 +35,7 @@ namespace FlaxEditor.Windows.Profiler
private Dictionary<string, Guid> _assetPathToId;
private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder;
private GPUResource[] _gpuResourcesCached;
public MemoryGPU()
: base("GPU Memory")
@@ -138,13 +139,15 @@ namespace FlaxEditor.Windows.Profiler
// Capture current GPU resources usage info
var contentDatabase = Editor.Instance.ContentDatabase;
var gpuResources = GPUDevice.Instance.Resources;
var resources = new Resource[gpuResources.Length];
GPUDevice.Instance.GetResources(ref _gpuResourcesCached, out var count);
var resources = new Resource[count];
var sb = _stringBuilder;
for (int i = 0; i < resources.Length; i++)
for (int i = 0; i < count; i++)
{
var gpuResource = gpuResources[i];
var gpuResource = _gpuResourcesCached[i];
ref var resource = ref resources[i];
if (!gpuResource)
continue;
// Try to reuse cached resource info
var gpuResourceId = gpuResource.ID;
@@ -219,6 +222,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null)
_resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources);
Array.Clear(_gpuResourcesCached);
}
/// <inheritdoc />
@@ -255,6 +259,7 @@ namespace FlaxEditor.Windows.Profiler
_assetPathToId?.Clear();
_tableRowsCache?.Clear();
_stringBuilder?.Clear();
_gpuResourcesCached = null;
base.OnDestroy();
}

View File

@@ -26,6 +26,7 @@ namespace FlaxEditor.Windows
private Tree _tree;
private Panel _sceneTreePanel;
private bool _isUpdatingSelection;
private bool _blockSceneTreeScroll = false;
private DragAssets _dragAssets;
private DragActorType _dragActorType;
@@ -33,6 +34,7 @@ namespace FlaxEditor.Windows
private DragScriptItems _dragScriptItems;
private DragHandlers _dragHandlers;
private bool _isDropping = false;
private bool _forceScrollNodeToView = false;
/// <summary>
/// Scene tree panel.
@@ -90,6 +92,15 @@ namespace FlaxEditor.Windows
_tree.SelectedChanged += Tree_OnSelectedChanged;
_tree.RightClick += OnTreeRightClick;
_tree.Parent = _sceneTreePanel;
_tree.AfterDeferredLayout += () =>
{
if (_forceScrollNodeToView)
{
_forceScrollNodeToView = false;
ScrollToSelectedNode();
}
};
headerPanel.Parent = this;
// Setup input actions
@@ -100,6 +111,34 @@ namespace FlaxEditor.Windows
InputActions.Add(options => options.LockFocusSelection, () => Editor.Windows.EditWin.Viewport.LockFocusSelection());
InputActions.Add(options => options.Rename, RenameSelection);
}
/// <inheritdoc />
public override void OnPlayBeginning()
{
base.OnPlayBeginning();
_blockSceneTreeScroll = true;
}
/// <inheritdoc />
public override void OnPlayBegin()
{
base.OnPlayBegin();
_blockSceneTreeScroll = false;
}
/// <inheritdoc />
public override void OnPlayEnding()
{
base.OnPlayEnding();
_blockSceneTreeScroll = true;
}
/// <inheritdoc />
public override void OnPlayEnd()
{
base.OnPlayEnd();
_blockSceneTreeScroll = true;
}
/// <summary>
/// Enables or disables vertical and horizontal scrolling on the scene tree panel.
@@ -141,6 +180,16 @@ namespace FlaxEditor.Windows
root.TreeNode.UpdateFilter(query);
_tree.UnlockChildrenRecursive();
// When keep the selected nodes in a view
var nodeSelection = _tree.Selection;
if (nodeSelection.Count != 0)
{
var node = nodeSelection[nodeSelection.Count - 1];
node.Expand(true);
_forceScrollNodeToView = true;
}
PerformLayout();
PerformLayout();
}
@@ -249,7 +298,7 @@ namespace FlaxEditor.Windows
_tree.Select(nodes);
// For single node selected scroll view so user can see it
if (nodes.Count == 1)
if (nodes.Count == 1 && !_blockSceneTreeScroll)
{
nodes[0].ExpandAllParents(true);
_sceneTreePanel.ScrollViewTo(nodes[0]);
@@ -259,6 +308,12 @@ namespace FlaxEditor.Windows
_isUpdatingSelection = false;
}
/// <inheritdoc />
public override void OnEditorStateChanged()
{
_blockSceneTreeScroll = Editor.StateMachine.ReloadingScriptsState.IsActive;
}
private bool ValidateDragAsset(AssetItem assetItem)
{
if (assetItem.IsOfType<SceneAsset>())

View File

@@ -42,6 +42,7 @@ namespace FlaxEditor.Windows.Search
if (value == _selectedItem || (value != null && !_matchedItems.Contains(value)))
return;
// Restore the previous selected item to the non-selected color
if (_selectedItem != null)
{
_selectedItem.BackgroundColor = Color.Transparent;
@@ -54,6 +55,7 @@ namespace FlaxEditor.Windows.Search
_selectedItem.BackgroundColor = Style.Current.BackgroundSelected;
if (_matchedItems.Count > VisibleItemCount)
{
_selectedItem.Focus();
_resultPanel.ScrollViewTo(_selectedItem, true);
}
}
@@ -180,39 +182,17 @@ namespace FlaxEditor.Windows.Search
switch (key)
{
case KeyboardKeys.ArrowDown:
{
if (_matchedItems.Count == 0)
return true;
int currentPos;
if (_selectedItem != null)
{
currentPos = _matchedItems.IndexOf(_selectedItem) + 1;
if (currentPos >= _matchedItems.Count)
currentPos--;
}
else
{
currentPos = 0;
}
SelectedItem = _matchedItems[currentPos];
return true;
}
case KeyboardKeys.ArrowUp:
{
if (_matchedItems.Count == 0)
return true;
int currentPos;
if (_selectedItem != null)
{
currentPos = _matchedItems.IndexOf(_selectedItem) - 1;
if (currentPos < 0)
currentPos = 0;
}
else
{
currentPos = 0;
}
SelectedItem = _matchedItems[currentPos];
var focusedIndex = _matchedItems.IndexOf(_selectedItem);
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, _matchedItems.Count - 1);
var nextItem = _matchedItems[nextIndex];
SelectedItem = nextItem;
return true;
}
case KeyboardKeys.Return:
@@ -234,6 +214,17 @@ namespace FlaxEditor.Windows.Search
Hide();
return true;
}
case KeyboardKeys.Backspace:
{
// Alow the user to quickly focus the searchbar
if (_searchBox != null && !_searchBox.IsFocused)
{
_searchBox.Focus();
_searchBox.SelectAll();
return true;
}
break;
}
}
return base.OnKeyDown(key);

View File

@@ -20,6 +20,11 @@ namespace FlaxEditor.Windows.Search
/// </summary>
protected Image _icon;
/// <summary>
/// The color of the accent strip.
/// </summary>
protected Color _accentColor;
/// <summary>
/// The item name.
/// </summary>
@@ -56,7 +61,7 @@ namespace FlaxEditor.Windows.Search
var icon = new Image
{
Size = new Float2(logoSize),
Location = new Float2(5, (height - logoSize) / 2)
Location = new Float2(7, (height - logoSize) / 2)
};
_icon = icon;
@@ -74,6 +79,20 @@ namespace FlaxEditor.Windows.Search
typeLabel.TextColor = Style.Current.ForegroundGrey;
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
// Select and focus the item on right click to prevent the search from being cleared
if (button == MouseButton.Right)
{
_finder.SelectedItem = this;
_finder.Hand = true;
Focus();
return true;
}
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
@@ -86,6 +105,15 @@ namespace FlaxEditor.Windows.Search
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
public override void Draw()
{
if (IsMouseOver)
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BackgroundHighlighted);
base.Draw();
}
/// <inheritdoc />
public override void OnMouseEnter(Float2 location)
{
@@ -93,12 +121,7 @@ namespace FlaxEditor.Windows.Search
var root = RootWindow;
if (root != null)
{
root.Cursor = CursorType.Hand;
}
_finder.SelectedItem = this;
_finder.Hand = true;
}
/// <inheritdoc />
@@ -128,6 +151,7 @@ namespace FlaxEditor.Windows.Search
{
_asset = item;
_asset.AddReference(this);
_accentColor = Editor.Instance.ContentDatabase.GetProxy(item).AccentColor;
}
/// <inheritdoc />
@@ -176,9 +200,7 @@ namespace FlaxEditor.Windows.Search
{
string importLocation = System.IO.Path.GetDirectoryName(importPath);
if (!string.IsNullOrEmpty(importLocation) && System.IO.Directory.Exists(importLocation))
{
cm.AddButton("Show import location", () => FileSystem.ShowFileExplorer(importLocation));
}
}
}
cm.AddSeparator();
@@ -212,6 +234,10 @@ namespace FlaxEditor.Windows.Search
// Draw icon
var iconRect = _icon.Bounds;
_asset.DrawThumbnail(ref iconRect);
// Draw color strip
var rect = iconRect with { Width = 2, Height = Height, Location = new Float2(2, 0) };
Render2D.FillRectangle(rect, _accentColor);
}
/// <inheritdoc />

View File

@@ -7,6 +7,7 @@
#include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Threading/TaskGraph.h"
class BehaviorSystem : public TaskGraphSystem
@@ -38,6 +39,7 @@ TaskGraphSystem* Behavior::System = nullptr;
void BehaviorSystem::Job(int32 index)
{
PROFILE_CPU_NAMED("Behavior.Job");
PROFILE_MEM(AI);
Behaviors[index]->UpdateAsync();
}
@@ -57,6 +59,7 @@ void BehaviorSystem::Execute(TaskGraph* graph)
bool BehaviorService::Init()
{
PROFILE_MEM(AI);
Behavior::System = New<BehaviorSystem>();
Engine::UpdateGraph->AddSystem(Behavior::System);
return false;
@@ -70,9 +73,9 @@ void BehaviorService::Dispose()
Behavior::Behavior(const SpawnParams& params)
: Script(params)
, Tree(this)
{
_knowledge.Behavior = this;
Tree.Changed.Bind<Behavior, &Behavior::ResetLogic>(this);
}
void Behavior::UpdateAsync()
@@ -172,6 +175,19 @@ void Behavior::OnDisable()
BehaviorServiceInstance.UpdateList.Remove(this);
}
void Behavior::OnAssetChanged(Asset* asset, void* caller)
{
ResetLogic();
}
void Behavior::OnAssetLoaded(Asset* asset, void* caller)
{
}
void Behavior::OnAssetUnloaded(Asset* asset, void* caller)
{
}
#if USE_EDITOR
bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior)

View File

@@ -11,7 +11,7 @@
/// <summary>
/// Behavior instance script that runs Behavior Tree execution.
/// </summary>
API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script
API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script, private IAssetReference
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE(Behavior);
@@ -92,6 +92,11 @@ public:
void OnDisable() override;
private:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
#if USE_EDITOR
// Editor-only utilities to debug nodes state.
API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior);

View File

@@ -4,6 +4,7 @@
#include "BehaviorTree.h"
#include "BehaviorTreeNodes.h"
#include "BehaviorKnowledgeSelector.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Scripting/ManagedCLR/MProperty.h"
@@ -144,6 +145,7 @@ BehaviorKnowledge::~BehaviorKnowledge()
void BehaviorKnowledge::InitMemory(BehaviorTree* tree)
{
PROFILE_MEM(AI);
if (Tree)
FreeMemory();
if (!tree)

View File

@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonSerializer.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "FlaxEngine.Gen.h"
#if USE_EDITOR
#include "Engine/Level/Level.h"
@@ -275,6 +276,7 @@ Asset::LoadResult BehaviorTree::load()
if (surfaceChunk == nullptr)
return LoadResult::MissingDataChunk;
MemoryReadStream surfaceStream(surfaceChunk->Get(), surfaceChunk->Size());
PROFILE_MEM(AI);
if (Graph.Load(&surfaceStream, true))
{
LOG(Warning, "Failed to load graph \'{0}\'", ToString());

View File

@@ -4,6 +4,7 @@
#include "AnimEvent.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Level/Actors/AnimatedModel.h"
#include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h"
@@ -69,6 +70,7 @@ AnimContinuousEvent::AnimContinuousEvent(const SpawnParams& params)
bool AnimationsService::Init()
{
PROFILE_MEM(Animations);
Animations::System = New<AnimationsSystem>();
Engine::UpdateGraph->AddSystem(Animations::System);
return false;
@@ -83,6 +85,7 @@ void AnimationsService::Dispose()
void AnimationsSystem::Job(int32 index)
{
PROFILE_CPU_NAMED("Animations.Job");
PROFILE_MEM(Animations);
auto animatedModel = AnimationManagerInstance.UpdateList[index];
if (CanUpdateModel(animatedModel))
{
@@ -147,6 +150,7 @@ void AnimationsSystem::PostExecute(TaskGraph* graph)
if (!Active)
return;
PROFILE_CPU_NAMED("Animations.PostExecute");
PROFILE_MEM(Animations);
// Update gameplay
for (int32 index = 0; index < AnimationManagerInstance.UpdateList.Count(); index++)

View File

@@ -6,6 +6,7 @@
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Graphics/Models/SkeletonData.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Threading/Threading.h"
extern void RetargetSkeletonPose(const SkeletonData& sourceSkeleton, const SkeletonData& targetSkeleton, const SkinnedModel::SkeletonMapping& mapping, const Transform* sourceNodes, Transform* targetNodes);

View File

@@ -7,6 +7,7 @@
#include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Audio/AudioClip.h"
#include "Engine/Graphics/PostProcessSettings.h"
#if USE_EDITOR
@@ -249,6 +250,7 @@ bool SceneAnimation::Save(const StringView& path)
Asset::LoadResult SceneAnimation::load()
{
TrackStatesCount = 0;
PROFILE_MEM(AnimationsData);
// Get the data chunk
if (LoadChunk(0))

View File

@@ -12,6 +12,7 @@
#include "Engine/Audio/AudioSource.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h"
#include "Engine/Scripting/ManagedCLR/MException.h"
@@ -151,6 +152,7 @@ void SceneAnimationPlayer::Tick(float dt)
SceneAnimation* anim = Animation.Get();
if (!anim || !anim->IsLoaded())
return;
PROFILE_MEM(Animations);
// Setup state
if (_tracks.Count() != anim->TrackStatesCount)
@@ -229,6 +231,7 @@ void SceneAnimationPlayer::MapTrack(const StringView& from, const Guid& to)
SceneAnimation* anim = Animation.Get();
if (!anim || !anim->IsLoaded())
return;
PROFILE_MEM(Animations);
for (int32 j = 0; j < anim->Tracks.Count(); j++)
{
const auto& track = anim->Tracks[j];

View File

@@ -8,6 +8,7 @@
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Level/Level.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Core/Log.h"
@@ -151,6 +152,7 @@ void Audio::SetEnableHRTF(bool value)
bool AudioService::Init()
{
PROFILE_CPU_NAMED("Audio.Init");
PROFILE_MEM(Audio);
const auto settings = AudioSettings::Get();
const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio;
@@ -211,6 +213,7 @@ bool AudioService::Init()
void AudioService::Update()
{
PROFILE_CPU_NAMED("Audio.Update");
PROFILE_MEM(Audio);
// Update the master volume
float masterVolume = MasterVolume;

View File

@@ -10,6 +10,7 @@
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Streaming/StreamingGroup.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Threading/Threading.h"
@@ -18,6 +19,7 @@ REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClip
bool AudioClip::StreamingTask::Run()
{
PROFILE_MEM(Audio);
AssetReference<AudioClip> ref = _asset.Get();
if (ref == nullptr || AudioBackend::Instance == nullptr)
return true;
@@ -318,6 +320,7 @@ bool AudioClip::init(AssetInitData& initData)
Asset::LoadResult AudioClip::load()
{
PROFILE_MEM(Audio);
#if !COMPILE_WITH_OGG_VORBIS
if (AudioHeader.Format == AudioFormat::Vorbis)
{

View File

@@ -21,9 +21,8 @@ AudioSource::AudioSource(const SpawnParams& params)
, _playOnStart(false)
, _startTime(0.0f)
, _allowSpatialization(true)
, Clip(this)
{
Clip.Changed.Bind<AudioSource, &AudioSource::OnClipChanged>(this);
Clip.Loaded.Bind<AudioSource, &AudioSource::OnClipLoaded>(this);
}
void AudioSource::SetVolume(float value)
@@ -264,7 +263,7 @@ void AudioSource::RequestStreamingBuffersUpdate()
_needToUpdateStreamingBuffers = true;
}
void AudioSource::OnClipChanged()
void AudioSource::OnAssetChanged(Asset* asset, void* caller)
{
Stop();
@@ -276,7 +275,7 @@ void AudioSource::OnClipChanged()
}
}
void AudioSource::OnClipLoaded()
void AudioSource::OnAssetLoaded(Asset* asset, void* caller)
{
if (!SourceID)
return;
@@ -302,6 +301,10 @@ void AudioSource::OnClipLoaded()
}
}
void AudioSource::OnAssetUnloaded(Asset* asset, void* caller)
{
}
bool AudioSource::UseStreaming() const
{
if (Clip == nullptr || Clip->WaitForLoaded())

View File

@@ -13,7 +13,7 @@
/// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity.
/// </remarks>
API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")")
class FLAXENGINE_API AudioSource : public Actor
class FLAXENGINE_API AudioSource : public Actor, IAssetReference
{
DECLARE_SCENE_OBJECT(AudioSource);
friend class AudioStreamingHandler;
@@ -293,8 +293,10 @@ public:
void RequestStreamingBuffersUpdate();
private:
void OnClipChanged();
void OnClipLoaded();
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
/// <summary>
/// Plays the audio source. Should have buffer(s) binded before.

View File

@@ -9,6 +9,7 @@
#include "Engine/Tools/AudioTool/AudioTool.h"
#include "Engine/Engine/Units.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Audio/Audio.h"
#include "Engine/Audio/AudioListener.h"
#include "Engine/Audio/AudioSource.h"
@@ -321,6 +322,8 @@ void AudioBackendOAL::Listener_ReinitializeAll()
uint32 AudioBackendOAL::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{
PROFILE_MEM(Audio);
uint32 sourceID = 0;
ALC::Source::Rebuild(sourceID, position, orientation, volume, pitch, pan, loop, spatial, attenuation, minDistance, doppler);
@@ -516,6 +519,7 @@ void AudioBackendOAL::Buffer_Delete(uint32 bufferID)
void AudioBackendOAL::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{
PROFILE_CPU();
PROFILE_MEM(Audio);
// Pick the format for the audio data (it might not be supported natively)
ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
@@ -625,6 +629,8 @@ AudioBackend::FeatureFlags AudioBackendOAL::Base_Features()
void AudioBackendOAL::Base_OnActiveDeviceChanged()
{
PROFILE_MEM(Audio);
// Cleanup
Array<ALC::AudioSourceState> states;
states.EnsureCapacity(Audio::Sources.Count());

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Audio/Audio.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerMemory.h"
#if PLATFORM_WINDOWS
// Tweak Win ver
@@ -232,6 +233,7 @@ void AudioBackendXAudio2::Listener_ReinitializeAll()
uint32 AudioBackendXAudio2::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
{
PROFILE_MEM(Audio);
ScopeLock lock(XAudio2::Locker);
// Get first free source
@@ -580,6 +582,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(uint32 sourceID)
uint32 AudioBackendXAudio2::Buffer_Create()
{
PROFILE_MEM(Audio);
uint32 bufferID;
ScopeLock lock(XAudio2::Locker);
@@ -618,6 +621,7 @@ void AudioBackendXAudio2::Buffer_Delete(uint32 bufferID)
void AudioBackendXAudio2::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
{
PROFILE_MEM(Audio);
CHECK(info.NumChannels <= MAX_INPUT_CHANNELS);
XAudio2::Locker.Lock();

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/LogContext.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Threading/ThreadLocal.h"
@@ -34,15 +35,18 @@ bool ContentDeprecated::Clear(bool newValue)
#endif
AssetReferenceBase::AssetReferenceBase(IAssetReference* owner)
: _owner(owner)
{
}
AssetReferenceBase::~AssetReferenceBase()
{
Asset* asset = _asset;
if (asset)
{
_asset = nullptr;
asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
asset->RemoveReference();
asset->RemoveReference(this);
}
}
@@ -51,52 +55,60 @@ String AssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : TEXT("<null>");
}
void AssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
if (_owner)
_owner->OnAssetChanged(asset, this);
}
void AssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Loaded();
if (_owner)
_owner->OnAssetLoaded(asset, this);
}
void AssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Unload();
OnSet(nullptr);
if (_owner)
_owner->OnAssetUnloaded(asset, this);
}
void AssetReferenceBase::OnSet(Asset* asset)
{
auto e = _asset;
if (e != asset)
{
if (e)
{
e->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
e->RemoveReference();
}
e->RemoveReference(this);
_asset = e = asset;
if (e)
{
e->AddReference();
e->OnLoaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
e->OnUnloaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
}
e->AddReference(this);
Changed();
if (_owner)
_owner->OnAssetChanged(asset, this);
if (e && e->IsLoaded())
{
Loaded();
if (_owner)
_owner->OnAssetLoaded(asset, this);
}
}
}
void AssetReferenceBase::OnLoaded(Asset* asset)
{
if (_asset != asset)
return;
Loaded();
}
void AssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
Unload();
OnSet(nullptr);
}
WeakAssetReferenceBase::~WeakAssetReferenceBase()
{
Asset* asset = _asset;
if (asset)
{
_asset = nullptr;
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
asset->RemoveReference(this, true);
}
}
@@ -105,36 +117,43 @@ String WeakAssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : TEXT("<null>");
}
void WeakAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
}
void WeakAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
}
void WeakAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
Unload();
asset->RemoveReference(this, true);
_asset = nullptr;
}
void WeakAssetReferenceBase::OnSet(Asset* asset)
{
auto e = _asset;
if (e != asset)
{
if (e)
e->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
e->RemoveReference(this, true);
_asset = e = asset;
if (e)
e->OnUnloaded.Bind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
e->AddReference(this, true);
}
}
void WeakAssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
Unload();
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
}
SoftAssetReferenceBase::~SoftAssetReferenceBase()
{
Asset* asset = _asset;
if (asset)
{
_asset = nullptr;
asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
asset->RemoveReference();
asset->RemoveReference(this);
}
#if !BUILD_RELEASE
_id = Guid::Empty;
@@ -146,22 +165,34 @@ String SoftAssetReferenceBase::ToString() const
return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>"));
}
void SoftAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
{
}
void SoftAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
{
}
void SoftAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
{
if (_asset != asset)
return;
_asset->RemoveReference(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
}
void SoftAssetReferenceBase::OnSet(Asset* asset)
{
if (_asset == asset)
return;
if (_asset)
{
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset->RemoveReference(this);
_asset = asset;
_id = asset ? asset->GetID() : Guid::Empty;
if (asset)
{
asset->AddReference();
asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
}
asset->AddReference(this);
Changed();
}
@@ -170,10 +201,7 @@ void SoftAssetReferenceBase::OnSet(const Guid& id)
if (_id == id)
return;
if (_asset)
{
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset->RemoveReference(this);
_asset = nullptr;
_id = id;
Changed();
@@ -184,21 +212,7 @@ void SoftAssetReferenceBase::OnResolve(const ScriptingTypeHandle& type)
ASSERT(!_asset);
_asset = ::LoadAsset(_id, type);
if (_asset)
{
_asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->AddReference();
}
}
void SoftAssetReferenceBase::OnUnloaded(Asset* asset)
{
if (_asset != asset)
return;
_asset->RemoveReference();
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
_asset->AddReference(this);
}
Asset::Asset(const SpawnParams& params, const AssetInfo* info)
@@ -216,6 +230,39 @@ int32 Asset::GetReferencesCount() const
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
}
void Asset::AddReference()
{
Platform::InterlockedIncrement(&_refCount);
}
void Asset::AddReference(IAssetReference* ref, bool week)
{
if (!week)
Platform::InterlockedIncrement(&_refCount);
if (ref)
{
//PROFILE_MEM(EngineDelegate); // Include references tracking memory within Delegate memory
ScopeLock lock(_referencesLocker);
_references.Add(ref);
}
}
void Asset::RemoveReference()
{
Platform::InterlockedDecrement(&_refCount);
}
void Asset::RemoveReference(IAssetReference* ref, bool week)
{
if (ref)
{
ScopeLock lock(_referencesLocker);
_references.Remove(ref);
}
if (!week)
Platform::InterlockedDecrement(&_refCount);
}
String Asset::ToString() const
{
return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath());
@@ -354,6 +401,7 @@ uint64 Asset::GetMemoryUsage() const
if (Platform::AtomicRead(&_loadingTask))
result += sizeof(ContentLoadTask);
result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType);
result += _references.Capacity() * sizeof(HashSet<IAssetReference*>::Bucket);
Locker.Unlock();
return result;
}
@@ -444,6 +492,9 @@ bool Asset::WaitForLoaded(double timeoutInMilliseconds) const
}
PROFILE_CPU();
ZoneColor(TracyWaitZoneColor);
const StringView path(GetPath());
ZoneText(*path, path.Length());
Content::WaitForTask(loadingTask, timeoutInMilliseconds);
@@ -528,6 +579,7 @@ ContentLoadTask* Asset::createLoadingTask()
void Asset::startLoading()
{
PROFILE_MEM(ContentAssets);
ASSERT(!IsLoaded());
ASSERT(Platform::AtomicRead(&_loadingTask) == 0);
auto loadingTask = createLoadingTask();
@@ -627,6 +679,9 @@ void Asset::onLoaded_MainThread()
ASSERT(IsInMainThread());
// Send event
ScopeLock lock(_referencesLocker);
for (const auto& e : _references)
e.Item->OnAssetLoaded(this, this);
OnLoaded(this);
}
@@ -640,6 +695,9 @@ void Asset::onUnload_MainThread()
CancelStreaming();
// Send event
ScopeLock lock(_referencesLocker);
for (const auto& e : _references)
e.Item->OnAssetUnloaded(this, this);
OnUnloaded(this);
}

View File

@@ -7,6 +7,7 @@
#include "Engine/Core/Types/String.h"
#include "Engine/Platform/CriticalSection.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Threading/ConcurrentSystemLocker.h"
#include "Config.h"
#include "Types.h"
@@ -18,6 +19,20 @@
public: \
explicit type(const SpawnParams& params, const AssetInfo* info)
// Utility interface for objects that reference asset and want to get notified about asset reference changes.
class FLAXENGINE_API IAssetReference
{
public:
virtual ~IAssetReference() = default;
// Asset reference got changed.
virtual void OnAssetChanged(Asset* asset, void* caller) = 0;
// Asset got loaded.
virtual void OnAssetLoaded(Asset* asset, void* caller) = 0;
// Asset gets unloaded.
virtual void OnAssetUnloaded(Asset* asset, void* caller) = 0;
};
/// <summary>
/// Asset objects base class.
/// </summary>
@@ -48,6 +63,9 @@ protected:
int8 _deleteFileOnUnload : 1; // Indicates that asset source file should be removed on asset unload
int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved)
HashSet<IAssetReference*> _references;
CriticalSection _referencesLocker; // TODO: convert into a single interlocked exchange for the current thread owning lock
public:
/// <summary>
/// Initializes a new instance of the <see cref="Asset"/> class.
@@ -88,18 +106,22 @@ public:
/// <summary>
/// Adds reference to that asset.
/// </summary>
FORCE_INLINE void AddReference()
{
Platform::InterlockedIncrement(&_refCount);
}
void AddReference();
/// <summary>
/// Adds reference to that asset.
/// </summary>
void AddReference(IAssetReference* ref, bool week = false);
/// <summary>
/// Removes reference from that asset.
/// </summary>
FORCE_INLINE void RemoveReference()
{
Platform::InterlockedDecrement(&_refCount);
}
void RemoveReference();
/// <summary>
/// Removes reference from that asset.
/// </summary>
void RemoveReference(IAssetReference* ref, bool week = false);
public:
/// <summary>

View File

@@ -7,10 +7,11 @@
/// <summary>
/// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events.
/// </summary>
class FLAXENGINE_API AssetReferenceBase
class FLAXENGINE_API AssetReferenceBase : public IAssetReference
{
protected:
Asset* _asset = nullptr;
IAssetReference* _owner = nullptr;
public:
/// <summary>
@@ -36,6 +37,12 @@ public:
/// </summary>
AssetReferenceBase() = default;
/// <summary>
/// Initializes a new instance of the <see cref="AssetReferenceBase"/> class.
/// </summary>
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
AssetReferenceBase(IAssetReference* owner);
/// <summary>
/// Finalizes an instance of the <see cref="AssetReferenceBase"/> class.
/// </summary>
@@ -63,10 +70,14 @@ public:
/// </summary>
String ToString() const;
public:
// [IAssetReference]
void OnAssetChanged(Asset* asset, void* caller) override;
void OnAssetLoaded(Asset* asset, void* caller) override;
void OnAssetUnloaded(Asset* asset, void* caller) override;
protected:
void OnSet(Asset* asset);
void OnLoaded(Asset* asset);
void OnUnloaded(Asset* asset);
};
/// <summary>
@@ -87,6 +98,13 @@ public:
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
explicit AssetReference(decltype(__nullptr))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
@@ -96,6 +114,15 @@ public:
OnSet((Asset*)asset);
}
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
explicit AssetReference(IAssetReference* owner)
: AssetReferenceBase(owner)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AssetReference"/> class.
/// </summary>

View File

@@ -9,6 +9,7 @@
#include "Engine/Animations/Animations.h"
#include "Engine/Animations/SceneAnimations/SceneAnimation.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Serialization/MemoryReadStream.h"
#if USE_EDITOR
@@ -598,6 +599,7 @@ void Animation::OnScriptingDispose()
Asset::LoadResult Animation::load()
{
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get stream with animations data

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Animations/Animations.h"
#include "Engine/Threading/Threading.h"
@@ -25,6 +26,7 @@ AnimationGraph::AnimationGraph(const SpawnParams& params, const AssetInfo* info)
Asset::LoadResult AnimationGraph::load()
{
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get stream with graph data
@@ -83,6 +85,7 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
Log::ArgumentNullException();
return true;
}
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Create Graph data

View File

@@ -8,6 +8,7 @@
#include "Engine/Serialization/MemoryWriteStream.h"
#endif
#include "Engine/Animations/Animations.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Threading/Threading.h"
@@ -20,6 +21,7 @@ AnimationGraphFunction::AnimationGraphFunction(const SpawnParams& params, const
Asset::LoadResult AnimationGraphFunction::load()
{
PROFILE_MEM(AnimationsData);
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
// Get graph data from chunk

View File

@@ -165,9 +165,13 @@ Asset::LoadResult Material::load()
MaterialGenerator generator;
generator.Error.Bind(&OnGeneratorError);
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION)
{
LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", name, _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION);
}
else
{
LOG(Info, "Updating material \'{0}\'...", name);
}
// Load or create material surface
MaterialLayer* layer;
@@ -410,16 +414,18 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Prepare
auto& info = _shaderHeader.Material.Info;
const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable;
const bool isOpaque = info.BlendMode == MaterialBlendMode::Opaque;
const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage;
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle;
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && !isOpaque) || info.Domain == MaterialDomain::Particle;
const bool useTess =
info.TessellationMode != TessellationMethod::None &&
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
const bool useDistortion =
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
info.BlendMode != MaterialBlendMode::Opaque &&
!isOpaque &&
EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) &&
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None;
const MaterialShadingModel shadingModel = info.ShadingModel == MaterialShadingModel::CustomLit ? MaterialShadingModel::Unlit : info.ShadingModel;
// @formatter:off
static const char* Numbers[] =
@@ -431,7 +437,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
// Setup shader macros
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] });
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)info.ShadingModel] });
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)shadingModel] });
options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] });
options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] });
@@ -488,7 +494,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] });
options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] });
options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] });
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] });
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && isOpaque ? 1 : 0] });
options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] });
#endif
}

View File

@@ -18,6 +18,7 @@
#include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Tools/ModelTool/ModelTool.h"
@@ -304,6 +305,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
Log::ArgumentOutOfRangeException();
return true;
}
PROFILE_MEM(GraphicsMeshes);
// Dispose previous data and disable streaming (will start data uploading tasks manually)
StopStreaming();
@@ -343,6 +345,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
bool Model::LoadHeader(ReadStream& stream, byte& headerVersion)
{
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadHeader(stream, headerVersion))
return true;
@@ -509,6 +512,7 @@ bool Model::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk
void Model::SetupMaterialSlots(int32 slotsCount)
{
PROFILE_MEM(GraphicsMeshes);
ModelBase::SetupMaterialSlots(slotsCount);
// Adjust meshes indices for slots
@@ -584,6 +588,8 @@ int32 Model::GetAllocatedResidency() const
Asset::LoadResult Model::load()
{
PROFILE_MEM(GraphicsMeshes);
// Get header chunk
auto chunk0 = GetChunk(0);
if (chunk0 == nullptr || chunk0->IsMissing())

View File

@@ -5,10 +5,12 @@
#include "Engine/Core/Math/Transform.h"
#include "Engine/Content/WeakAssetReference.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Graphics/Config.h"
#include "Engine/Graphics/Models/MeshBase.h"
#include "Engine/Graphics/Models/MeshDeformation.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
#include "Engine/Threading/Threading.h"
#if GPU_ENABLE_ASYNC_RESOURCES_CREATION
#include "Engine/Threading/ThreadPoolTask.h"
#define STREAM_TASK_BASE ThreadPoolTask
@@ -51,6 +53,7 @@ public:
AssetReference<ModelBase> model = _model.Get();
if (model == nullptr)
return true;
PROFILE_MEM(GraphicsMeshes);
// Get data
BytesContainer data;
@@ -334,6 +337,8 @@ bool ModelBase::LoadHeader(ReadStream& stream, byte& headerVersion)
bool ModelBase::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
{
PROFILE_MEM(GraphicsMeshes);
// Load descriptor
static_assert(MODEL_MESH_VERSION == 2, "Update code");
uint32 vertices, triangles;

View File

@@ -6,6 +6,7 @@
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/SkeletonMaskUpgrader.h"
#include "Engine/Threading/Threading.h"
REGISTER_BINARY_ASSET_WITH_UPGRADER(SkeletonMask, "FlaxEngine.SkeletonMask", SkeletonMaskUpgrader, true);

View File

@@ -18,6 +18,7 @@
#include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/DrawCall.h"
#if USE_EDITOR
#include "Engine/Graphics/Models/ModelData.h"
@@ -458,6 +459,7 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
Log::ArgumentOutOfRangeException();
return true;
}
PROFILE_MEM(GraphicsMeshes);
// Dispose previous data and disable streaming (will start data uploading tasks manually)
StopStreaming();
@@ -501,6 +503,7 @@ void BlendShape::LoadHeader(ReadStream& stream, byte headerVersion)
void BlendShape::Load(ReadStream& stream, byte meshVersion)
{
PROFILE_MEM(GraphicsMeshes);
UseNormals = stream.ReadBool();
stream.ReadUint32(&MinVertexIndex);
stream.ReadUint32(&MaxVertexIndex);
@@ -531,6 +534,7 @@ void BlendShape::Save(WriteStream& stream) const
bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
{
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadMesh(stream, meshVersion, mesh, dataIfReadOnly))
return true;
static_assert(MODEL_MESH_VERSION == 2, "Update code");
@@ -560,6 +564,7 @@ bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase
bool SkinnedModel::LoadHeader(ReadStream& stream, byte& headerVersion)
{
PROFILE_MEM(GraphicsMeshes);
if (ModelBase::LoadHeader(stream, headerVersion))
return true;
static_assert(MODEL_HEADER_VERSION == 2, "Update code");
@@ -861,6 +866,7 @@ uint64 SkinnedModel::GetMemoryUsage() const
void SkinnedModel::SetupMaterialSlots(int32 slotsCount)
{
PROFILE_MEM(GraphicsMeshes);
ModelBase::SetupMaterialSlots(slotsCount);
// Adjust meshes indices for slots
@@ -954,6 +960,7 @@ Asset::LoadResult SkinnedModel::load()
if (chunk0 == nullptr || chunk0->IsMissing())
return LoadResult::MissingDataChunk;
MemoryReadStream headerStream(chunk0->Get(), chunk0->Size());
PROFILE_MEM(GraphicsMeshes);
// Load asset data (anything but mesh contents that use streaming)
byte headerVersion;

View File

@@ -18,6 +18,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Serialization/JsonWriter.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Level/SceneObject.h"
@@ -37,10 +38,12 @@ namespace
void PrintStack(LogType type)
{
#if LOG_ENABLE
const String stack = VisualScripting::GetStackTrace();
Log::Logger::Write(type, TEXT("Visual Script stack trace:"));
Log::Logger::Write(type, stack);
Log::Logger::Write(type, TEXT(""));
#endif
}
bool SerializeValue(const Variant& a, const Variant& b)
@@ -1340,6 +1343,8 @@ bool VisualScript::Save(const StringView& path)
Asset::LoadResult VisualScript::load()
{
PROFILE_MEM(ScriptingVisual);
// Build Visual Script typename that is based on asset id
String typeName = _id.ToString();
StringUtils::ConvertUTF162ANSI(typeName.Get(), _typenameChars, 32);
@@ -1532,6 +1537,7 @@ Asset::LoadResult VisualScript::load()
void VisualScript::unload(bool isReloading)
{
PROFILE_MEM(ScriptingVisual);
#if USE_EDITOR
if (isReloading)
{
@@ -1588,6 +1594,7 @@ AssetChunksFlag VisualScript::getChunksToPreload() const
void VisualScript::CacheScriptingType()
{
PROFILE_MEM(ScriptingVisual);
ScopeLock lock(VisualScriptingBinaryModule::Locker);
auto& binaryModule = VisualScriptingModule;
@@ -1723,6 +1730,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex];
// Initialize instance data
PROFILE_MEM(ScriptingVisual);
ScopeLock lock(visualScript->Locker);
auto& instanceParams = visualScript->_instances[object->GetID()].Params;
instanceParams.Resize(visualScript->Graph.Parameters.Count());
@@ -1747,6 +1755,8 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
void VisualScriptingBinaryModule::OnScriptsReloading()
{
PROFILE_MEM(ScriptingVisual);
// Clear any cached types from that module across all loaded Visual Scripts
for (auto& script : Scripts)
{
@@ -1795,6 +1805,7 @@ void VisualScriptingBinaryModule::OnScriptsReloading()
void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant> parameters, ScriptingTypeHandle eventType, StringView eventName)
{
PROFILE_MEM(ScriptingVisual);
if (object)
{
// Object event
@@ -1900,9 +1911,13 @@ bool VisualScriptingBinaryModule::InvokeMethod(void* method, const Variant& inst
if (!instanceObject || instanceObject->GetTypeHandle() != vsMethod->Script->GetScriptingType())
{
if (!instanceObject)
{
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count());
}
else
{
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count(), String(instanceObject->GetType().Fullname));
}
return true;
}
}
@@ -1952,6 +1967,7 @@ bool VisualScriptingBinaryModule::GetFieldValue(void* field, const Variant& inst
bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& instance, Variant& value)
{
PROFILE_MEM(ScriptingVisual);
const auto vsFiled = (VisualScript::Field*)field;
const auto instanceObject = (ScriptingObject*)instance;
if (!instanceObject)
@@ -2038,6 +2054,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier)
{
PROFILE_MEM(ScriptingVisual);
ASSERT(stream.IsObject());
Locker.Lock();
const auto asset = Scripts[object->GetTypeHandle().TypeIndex].Get();
@@ -2161,6 +2178,7 @@ const Variant& VisualScript::GetScriptInstanceParameterValue(const StringView& n
void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, const Variant& value)
{
PROFILE_MEM(ScriptingVisual);
CHECK(instance);
for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++)
{
@@ -2182,6 +2200,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip
void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, Variant&& value)
{
PROFILE_MEM(ScriptingVisual);
CHECK(instance);
for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++)
{
@@ -2379,6 +2398,7 @@ VisualScriptingBinaryModule* VisualScripting::GetBinaryModule()
Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters)
{
PROFILE_MEM(ScriptingVisual);
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
PROFILE_CPU_SRC_LOC(method->ProfilerData);
@@ -2419,6 +2439,7 @@ bool VisualScripting::Evaluate(VisualScript* script, ScriptingObject* instance,
const auto box = node->GetBox(boxId);
if (!box)
return false;
PROFILE_MEM(ScriptingVisual);
// Add to the calling stack
ScopeContext scope;

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