Compare commits

...

171 Commits

Author SHA1 Message Date
mafiesto4 c486577b07 Fix TAA in editor's debug view modes 2024-05-20 19:03:28 +02:00
mafiesto4 a69c8ce6a2 Revert 73f68c102d and reopen #2610 2024-05-20 18:37:15 +02:00
mafiesto4 e527783e55 Fix missing curve initialization when loading from json 2024-05-20 18:36:41 +02:00
mafiesto4 4d9c92dd49 Fix editor hang when scene deserialization fails 2024-05-20 18:24:20 +02:00
mafiesto4 9b01229e58 Fix regression in GPU Particle collisions in editor 2024-05-20 18:12:35 +02:00
mafiesto4 e3a030fad8 Update version 2024-05-20 17:10:23 +02:00
mafiesto4 cb878294ea Minor fixes 2024-05-20 17:09:59 +02:00
mafiesto4 1bd1aca0f0 Merge branch 'GoaLitiuM-richtextbox_fix' 2024-05-20 14:43:26 +02:00
mafiesto4 b433312042 Merge branch 'richtextbox_fix' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-richtextbox_fix 2024-05-20 14:43:09 +02:00
mafiesto4 1041b1b86d Merge branch 'tecnessino-patch-2' 2024-05-20 14:23:20 +02:00
mafiesto4 1ace5fd10d Merge branch 'patch-2' of https://github.com/tecnessino/FlaxEngine into tecnessino-patch-2 2024-05-20 14:23:03 +02:00
mafiesto4 cfc9f73744 Fix deadlock between content storage and asset that is being updated during load
#2621
2024-05-20 14:22:03 +02:00
mafiesto4 2418167182 Merge branch 'Tryibion-update-cursor-unlock-text' 2024-05-20 11:47:07 +02:00
mafiesto4 52090d3a6b Merge branch 'update-cursor-unlock-text' of https://github.com/Tryibion/FlaxEngine into Tryibion-update-cursor-unlock-text 2024-05-20 11:47:01 +02:00
mafiesto4 73f68c102d Revert #2421 two bone ik to solve #2610 regression 2024-05-20 11:19:03 +02:00
tecnessino 862dd1e5f1 Change comment 2024-05-18 20:37:43 +02:00
GoaLitiuM 58351d1989 Fix RichTextBox not drawing the last character 2024-05-18 00:00:27 +03:00
Tryibion 6705205e2f Update cursor unlock text with correct input key bind. 2024-05-17 09:41:02 -05:00
mafiesto4 2cdd0ff644 Fix test compilation 2024-05-17 15:47:50 +02:00
mafiesto4 69ae841f64 Merge branch 'Tryibion-sel-prefab-clear-search' 2024-05-17 15:31:04 +02:00
mafiesto4 7f8700288f Merge branch 'sel-prefab-clear-search' of https://github.com/Tryibion/FlaxEngine into Tryibion-sel-prefab-clear-search 2024-05-17 15:30:59 +02:00
mafiesto4 f87dec6ca6 Merge branch 'Tryibion-edit-options-move' 2024-05-17 15:30:50 +02:00
mafiesto4 65a6c0aed5 Merge branch 'edit-options-move' of https://github.com/Tryibion/FlaxEngine into Tryibion-edit-options-move 2024-05-17 15:30:44 +02:00
mafiesto4 f6dd0decfb Merge branch 'Tryibion-prefab-camera' 2024-05-17 15:30:26 +02:00
mafiesto4 816984542a Merge branch 'prefab-camera' of https://github.com/Tryibion/FlaxEngine into Tryibion-prefab-camera 2024-05-17 15:30:18 +02:00
Tryibion 3837e8b263 Rename "Options" to "Editor Options" and move under the "Edit" menu. 2024-05-16 23:20:44 -05:00
Tryibion 47b3141f18 Clear item search on select prefab 2024-05-16 23:00:32 -05:00
Tryibion e10ee3e55a Force camera model to load in prefabs. 2024-05-16 16:49:56 -05:00
mafiesto4 0a4e89e29b Fix UI navigation when using multiple canvases
#2574
2024-05-16 16:12:53 +02:00
mafiesto4 0765fa92b5 Add option to move all selected UI Controls while holding Shift key 2024-05-16 14:11:11 +02:00
mafiesto4 2529312152 Refactor Color.FromRGBA and add matching old logic Color.FromARGB
2592
2024-05-16 13:45:29 +02:00
mafiesto4 3404643636 Add better error messages to NetworkReplicator::SetObjectOwnership for incorrect usage 2024-05-16 10:49:51 +02:00
mafiesto4 6b9f6ac82e Fix incorrect async methods used as RPCs (not supported now) 2024-05-16 10:35:17 +02:00
mafiesto4 ab5bb79754 Fix regression in collections editing after improving ReadOnly option 2024-05-16 10:34:57 +02:00
mafiesto4 58f95d6ce3 Simplify 6f2bd0e932 2024-05-15 23:49:46 +02:00
mafiesto4 1cd2f6a070 Fix compilation when using non-strongly typed enum as default value 2024-05-15 13:23:30 +02:00
mafiesto4 35ddfc2455 Add test case of nested types in scripting
#2582 #2591
2024-05-15 13:09:51 +02:00
mafiesto4 17d1d87268 Merge branch 'GoaLitiuM-nested_type_fixes' 2024-05-15 12:51:57 +02:00
mafiesto4 fd871ce830 Merge branch 'nested_type_fixes' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-nested_type_fixes 2024-05-15 12:50:44 +02:00
mafiesto4 b4a4a8a591 Minor code cleanup 2024-05-15 12:40:08 +02:00
mafiesto4 24e4015425 Merge branch 'Tryibion-font-case' 2024-05-15 12:30:43 +02:00
mafiesto4 c670887b1a Merge branch 'font-case' of https://github.com/Tryibion/FlaxEngine into Tryibion-font-case 2024-05-15 12:30:39 +02:00
mafiesto4 d3cd6a461b Merge branch 'Tryibion-ui-edit-handles' 2024-05-15 12:26:25 +02:00
mafiesto4 2625a9d762 Merge branch 'ui-edit-handles' of https://github.com/Tryibion/FlaxEngine into Tryibion-ui-edit-handles 2024-05-15 12:26:20 +02:00
mafiesto4 4fdd9a242b Merge branch 'Tryibion-add-script' 2024-05-15 12:26:06 +02:00
mafiesto4 a6e8e6f749 Merge branch 'add-script' of https://github.com/Tryibion/FlaxEngine into Tryibion-add-script 2024-05-15 12:25:59 +02:00
mafiesto4 03eabbcf63 Merge branch 'Tryibion-control-trans-dup' 2024-05-15 12:25:34 +02:00
mafiesto4 13f94dcf11 Merge branch 'control-trans-dup' of https://github.com/Tryibion/FlaxEngine into Tryibion-control-trans-dup 2024-05-15 12:25:28 +02:00
mafiesto4 3b44062eb0 Merge branch 'Tryibion-checkerboard-colorpicker' 2024-05-15 12:25:13 +02:00
mafiesto4 1457637707 Merge branch 'checkerboard-colorpicker' of https://github.com/Tryibion/FlaxEngine into Tryibion-checkerboard-colorpicker 2024-05-15 12:25:08 +02:00
mafiesto4 d28567111f Merge branch 'Tryibion-treenode-guidelines' 2024-05-15 12:24:17 +02:00
mafiesto4 1c7f06e570 Merge branch 'treenode-guidelines' of https://github.com/Tryibion/FlaxEngine into Tryibion-treenode-guidelines 2024-05-15 12:24:12 +02:00
mafiesto4 63cc0fef2e Merge branch 'Tryibion-tooltip-text-padding' 2024-05-15 12:23:52 +02:00
mafiesto4 57084b3d6c Merge branch 'tooltip-text-padding' of https://github.com/Tryibion/FlaxEngine into Tryibion-tooltip-text-padding 2024-05-15 12:23:47 +02:00
mafiesto4 fa23619f08 Merge branch 'Tryibion-scroll-colors' 2024-05-15 12:23:12 +02:00
mafiesto4 1f2456fc67 Merge branch 'scroll-colors' of https://github.com/Tryibion/FlaxEngine into Tryibion-scroll-colors 2024-05-15 12:23:08 +02:00
mafiesto4 0b71e906a6 Merge branch 'Tryibion-textbox-add' 2024-05-15 12:22:47 +02:00
mafiesto4 2e59c35a44 Merge branch 'textbox-add' of https://github.com/Tryibion/FlaxEngine into Tryibion-textbox-add 2024-05-15 12:22:40 +02:00
mafiesto4 6f2bd0e932 Another attempt on 6a883bc7c6 2024-05-15 11:22:07 +02:00
mafiesto4 6a883bc7c6 Revert file handles bug 2024-05-14 09:03:52 +02:00
Tryibion 17de6388ca Code fix 2024-05-13 17:42:37 -05:00
Tryibion e028d263f1 Remove unused include. 2024-05-13 16:38:38 -05:00
Tryibion 6962ed6730 Fix case spelling 2024-05-13 16:38:10 -05:00
Tryibion b66d50ae1b Add font and case options to Label and Textbox 2024-05-13 16:34:23 -05:00
Tryibion 675ce71935 Draw hover before selection so that way ui selection is drawn on top. 2024-05-13 12:45:59 -05:00
Tryibion 833f844d59 Minor improvements to ui editor widget drawing. 2024-05-13 12:38:11 -05:00
mafiesto4 af08dc1c69 Fix ReadOnly attribute handling in collection editors 2024-05-13 16:08:50 +02:00
Tryibion 07628d2ec7 Clean up code 2024-05-11 21:31:00 -05:00
Tryibion aac3dbfe09 Fix control transform duplicating when changing anchor. 2024-05-11 21:29:45 -05:00
Tryibion 185f24ce49 Fix value alpha transparency being applied twice to preview color. 2024-05-11 20:19:51 -05:00
GoaLitiuM 77e29109ee Fix native internal type name for deeply nested types 2024-05-11 15:48:49 +03:00
GoaLitiuM 8d89b9efb0 Fix internal type name for nested blittable element types 2024-05-11 15:48:43 +03:00
Tryibion b2fee31a13 Add checkerboard background to color picker dialog color. 2024-05-10 20:30:14 -05:00
Tryibion fc7628e2ee Add tree node guidlines 2024-05-10 19:22:23 -05:00
Tryibion 2e3e4959d6 Add width padding for tooltip 2024-05-10 17:18:10 -05:00
Tryibion f22105c2c3 Allow adding script via drag drop 2024-05-10 16:58:40 -05:00
Tryibion 533902d185 Change panel scroll bar style editor order. 2024-05-10 15:04:27 -05:00
mafiesto4 68653fa91f Add small code optimizations 2024-05-10 12:27:28 +02:00
Tryibion dc0aa61a14 Add scrollbar colors to Panel 2024-05-09 22:14:08 -05:00
Tryibion ee790ff3a9 Change colors for tabs to be seen better. 2024-05-09 21:53:22 -05:00
Tryibion a2a3926aee Expose colors in scrollbar 2024-05-09 21:53:04 -05:00
Tryibion 9a70344c1f Fix carrot location on text box with no text. Add vertical and horizontal alignment options to textbox. 2024-05-09 21:31:54 -05:00
mafiesto4 44006dd533 Various code improvements 2024-05-09 18:59:09 +02:00
mafiesto4 f6aabf2d14 Optimize navmesh building and reduce scene lock time 2024-05-09 18:58:47 +02:00
mafiesto4 dc1f15f18d Add OnStaticFlagsChanged to Actor 2024-05-09 16:55:05 +02:00
mafiesto4 7d7808af8f Remove default value tooltips from post process docs to prevent errors 2024-05-09 10:56:42 +02:00
mafiesto4 5029584a9f Fix crash when Visual Script event binding instance is invalid
#2548
2024-05-09 10:17:23 +02:00
mafiesto4 f353d3f114 Fix looping root motion 2024-05-09 10:16:45 +02:00
mafiesto4 667e8bc293 Minor cleanup in anim code 2024-05-09 10:16:34 +02:00
mafiesto4 2edb9cc4d8 Fix compilation warnings when using 64-bit coords 2024-05-08 23:24:23 +02:00
mafiesto4 7018666a8c Add layer masking for PostFxVolume against Camera's RenderLayersMask
#2573
2024-05-08 22:42:35 +02:00
mafiesto4 f04926ad94 Merge branch 'GoaLitiuM-treenode_expanded_fix' 2024-05-08 19:30:55 +02:00
GoaLitiuM 50f5f0acd9 Fix TreeNode rendering issues with expanded nodes 2024-05-08 20:15:55 +03:00
mafiesto4 3745979b81 Add attributes for game into AndroidManifest 2024-05-08 18:49:16 +02:00
mafiesto4 db15f6f08a Add For Distribution to be enabled by default in Release builds
#2571
2024-05-08 18:48:55 +02:00
mafiesto4 e1a2f51d5a Merge branch 'GoaLitiuM-editor_font_validation' 2024-05-08 18:17:53 +02:00
mafiesto4 a8e1fd7a4a Merge branch 'editor_font_validation' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-editor_font_validation 2024-05-08 18:17:46 +02:00
mafiesto4 d46ef6ac92 Merge branch 'Tryibion-add-shift-scroll' 2024-05-08 18:17:20 +02:00
mafiesto4 36d21b27c7 Merge branch 'add-shift-scroll' of https://github.com/Tryibion/FlaxEngine into Tryibion-add-shift-scroll 2024-05-08 18:17:13 +02:00
mafiesto4 b1636c27e7 Merge branch 'Tryibion-Move-control-transform' 2024-05-08 17:39:48 +02:00
mafiesto4 5d32fc6c5e Merge branch 'Move-control-transform' of https://github.com/Tryibion/FlaxEngine into Tryibion-Move-control-transform 2024-05-08 17:31:47 +02:00
mafiesto4 065dc474c0 Merge branch 'Tryibion-game-window-focus-issue' 2024-05-08 17:31:16 +02:00
mafiesto4 1fb7b24aad Merge branch 'game-window-focus-issue' of https://github.com/Tryibion/FlaxEngine into Tryibion-game-window-focus-issue 2024-05-08 17:31:08 +02:00
mafiesto4 f0b72aa025 Merge branch 'Tryibion-color-picker' 2024-05-08 17:30:50 +02:00
mafiesto4 058077736b Merge branch 'color-picker' of https://github.com/Tryibion/FlaxEngine into Tryibion-color-picker 2024-05-08 17:30:45 +02:00
mafiesto4 b02f011627 Merge branch 'GoaLitiuM-drawtext_fix' 2024-05-08 10:34:33 +02:00
GoaLitiuM ea04c746fd Fix Editor not launching if custom interface fonts are missing 2024-05-07 21:18:09 +03:00
GoaLitiuM 97454fc82e Fix drawing an extra character with Render2D.DrawText 2024-05-07 20:10:05 +03:00
Tryibion 4a6afdb108 Small fixes for color picker. 2024-05-07 08:41:23 -05:00
mafiesto4 65e852600a Merge branch 'Tryibion-skip-empty-layers' 2024-05-05 22:56:47 +02:00
mafiesto4 fedd990c13 Merge branch 'skip-empty-layers' of https://github.com/Tryibion/FlaxEngine into Tryibion-skip-empty-layers 2024-05-05 22:56:41 +02:00
mafiesto4 c0329abe40 Merge branch 'Tryibion-fix-properties-1st-offset' 2024-05-05 22:55:30 +02:00
mafiesto4 d8850a56a8 Merge branch 'fix-properties-1st-offset' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-properties-1st-offset 2024-05-05 22:55:26 +02:00
mafiesto4 e171bb06ec Merge branch 'GoaLitiuM-unbox_array_fix' 2024-05-05 22:54:18 +02:00
mafiesto4 3825e07adc Merge branch 'unbox_array_fix' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-unbox_array_fix 2024-05-05 22:54:12 +02:00
mafiesto4 db8adf7d96 Fix creating virtual terrain collision of actor that is not in a game 2024-05-05 22:50:12 +02:00
GoaLitiuM e77ae12b9b Fix unboxing of array type Variants 2024-05-05 13:15:54 +03:00
Tryibion bf4e4aeaf6 Fix bottom offset for last property label and property 2024-05-04 22:32:00 -05:00
Tryibion 2107b069db Fix odd offset between 1st property and property label. 2024-05-04 19:25:56 -05:00
Tryibion ea2005dacb Add layer number to LayerMask UI 2024-05-04 18:08:53 -05:00
Tryibion d5cded8aaa Skip empty layers for LayerMask Editor 2024-05-04 17:51:27 -05:00
Tryibion 430b22d5d7 Dont unfocus game window while cursor is not visible unless the lock mode is none. 2024-05-04 17:37:32 -05:00
mafiesto4 9d830eb1e2 Fix crash when using scripting object as interface implementation in object property displayed in Editor
#2493
2024-05-02 18:31:30 +02:00
mafiesto4 7e3f84f95e Merge branch 'Tryibion-gg-keep-last-type' 2024-05-02 15:57:50 +02:00
mafiesto4 cddee38d71 Merge branch 'gg-keep-last-type' of https://github.com/Tryibion/FlaxEngine into Tryibion-gg-keep-last-type 2024-05-02 15:57:45 +02:00
mafiesto4 e030d0461b Merge branch 'Tryibion-smoothstep-node-width' 2024-05-02 15:57:31 +02:00
mafiesto4 4978c8e0d9 Merge branch 'smoothstep-node-width' of https://github.com/Tryibion/FlaxEngine into Tryibion-smoothstep-node-width 2024-05-02 15:57:20 +02:00
mafiesto4 dc7b7e6e10 Merge branch 'Tryibion-rename-trim' 2024-05-02 15:56:33 +02:00
mafiesto4 1e3eb11b94 Merge branch 'rename-trim' of https://github.com/Tryibion/FlaxEngine into Tryibion-rename-trim 2024-05-02 15:56:25 +02:00
mafiesto4 b15b231b85 Merge branch 'Tryibion-windows-hide-cursor' 2024-05-02 15:55:56 +02:00
mafiesto4 262992571a Merge branch 'windows-hide-cursor' of https://github.com/Tryibion/FlaxEngine into Tryibion-windows-hide-cursor 2024-05-02 15:55:50 +02:00
mafiesto4 352bf3f9a7 Merge branch 'Tryibion-fix-toolbox-panel-size' 2024-05-02 15:55:29 +02:00
Tryibion 9683868767 Add shift scroll for panels and textbox 2024-05-01 16:12:53 -05:00
Tryibion 40284fbbf8 Fix smooth step visject node width 2024-05-01 14:05:10 -05:00
Tryibion 0c86a900da Default gameplay global type selection to last selected type. 2024-05-01 13:54:41 -05:00
Tryibion c1e3eaeab1 Force windows cursor to show or hide based on cursor hidden value 2024-05-01 13:23:56 -05:00
Tryibion 3c487dff47 Trim actor name on rename. 2024-05-01 12:01:01 -05:00
Tryibion 2260d79e26 Trim content item name on rename 2024-05-01 11:54:07 -05:00
Tryibion 3209320547 Add margin to item tree in toolbox 2024-04-30 15:05:58 -05:00
Tryibion d1db06a9bb Change text alignment to far on some control transform properties 2024-04-29 19:44:49 -05:00
Tryibion 1c1d2fd96f Fix still showing some control transform properties in wrong spot. 2024-04-29 19:38:36 -05:00
Tryibion 2e5ad8c48a Always set control transform under general tab 2024-04-29 18:28:04 -05:00
Tryibion 9a6f866956 Move control transform to be under general group in layout. 2024-04-29 18:24:01 -05:00
mafiesto4 c59bce3b58 Merge branch 'Tryibion-controls-toolbox' 2024-04-29 18:37:11 +02:00
mafiesto4 1185a9c06c Merge branch 'controls-toolbox' of https://github.com/Tryibion/FlaxEngine into Tryibion-controls-toolbox
# Conflicts:
#	Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
2024-04-29 18:37:04 +02:00
Tryibion 026b69c544 Merge branch 'master' into controls-toolbox 2024-04-29 11:34:03 -05:00
mafiesto4 df7ece7655 Merge branch 'Tryibion-controls-toolbox' 2024-04-29 18:24:36 +02:00
mafiesto4 7e8f20bd9b Merge branch 'controls-toolbox' of https://github.com/Tryibion/FlaxEngine into Tryibion-controls-toolbox
# Conflicts:
#	Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
2024-04-29 16:56:30 +02:00
mafiesto4 20aef27439 Merge branch 'Tryibion-watermark-attribute' 2024-04-29 12:23:06 +02:00
mafiesto4 12344cf725 Merge branch 'watermark-attribute' of https://github.com/Tryibion/FlaxEngine into Tryibion-watermark-attribute 2024-04-29 12:23:01 +02:00
mafiesto4 ebb2704726 Merge branch 'Tryibion-fix-caret' 2024-04-29 12:21:55 +02:00
mafiesto4 5f9d0140c7 Merge branch 'fix-caret' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-caret 2024-04-29 12:21:49 +02:00
mafiesto4 02a7f74993 Merge branch 'Tryibion-focus-window-on-spawn' 2024-04-29 10:44:16 +02:00
mafiesto4 452e12db45 Merge branch 'focus-window-on-spawn' of https://github.com/Tryibion/FlaxEngine into Tryibion-focus-window-on-spawn 2024-04-29 10:44:09 +02:00
mafiesto4 ac46c89904 Merge branch 'Tryibion-fix-actortype-drag-spawn' 2024-04-29 10:42:38 +02:00
mafiesto4 88acc772b3 Merge branch 'fix-actortype-drag-spawn' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-actortype-drag-spawn 2024-04-29 10:42:31 +02:00
mafiesto4 fbec80c801 Fix error when using undo after removing Multi Blend node 2024-04-29 10:35:32 +02:00
mafiesto4 1ef487a5cc Add removing selected blend space point with Delete key
#2505
2024-04-29 10:35:07 +02:00
mafiesto4 1e5861de25 Merge branch 'Tryibion-win-layout-name' 2024-04-29 10:19:48 +02:00
mafiesto4 07733a4efb Merge branch 'win-layout-name' of https://github.com/Tryibion/FlaxEngine into Tryibion-win-layout-name 2024-04-29 10:19:38 +02:00
mafiesto4 fee15846ba Merge branch 'GoaLitiuM-fix_treenode_rendering' 2024-04-29 10:18:06 +02:00
mafiesto4 7040da9a44 Merge branch 'fix_treenode_rendering' of https://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-fix_treenode_rendering 2024-04-29 10:17:58 +02:00
GoaLitiuM a8a621df3b Fix TreeNode not rendering all nodes properly with invisible children 2024-04-28 20:30:41 +03:00
Tryibion 18660140b0 Add controls to the ActorToolbox in the GUI tab. Add dragging and dropping controls from toolbox into scene and prefab trees. 2024-04-27 15:55:25 -05:00
Tryibion 58cc53b44d Remove unused include 2024-04-27 11:10:27 -05:00
Tryibion d8bb831dd9 Add watermark attribute to add watermark to string fields in editor. Have watermark show even when textbox is focused . 2024-04-27 11:07:20 -05:00
Tryibion 0d770e3909 Focus prefab window on item spawn. 2024-04-27 09:56:51 -05:00
Tryibion 3641886ebf Fix textbox caret showing on empty string. 2024-04-27 09:16:03 -05:00
Tryibion 611dd7bad1 Change "Apply window layouts" to "Window layouts" 2024-04-27 09:01:46 -05:00
Tryibion 7db20e0411 Fix actor type drag spawn to correctly assign parent. 2024-04-27 08:44:20 -05:00
mafiesto4 e835b25637 Fix wired Vulkan validation cache errors on Linux
#2201 #1825
2024-04-26 21:47:51 +02:00
151 changed files with 1811 additions and 542 deletions
+2 -2
View File
@@ -3,8 +3,8 @@
"Version": {
"Major": 1,
"Minor": 8,
"Revision": 1,
"Build": 6511
"Revision": 2,
"Build": 6512
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
+1
View File
@@ -74,6 +74,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=UNION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=UNION_005FMEMBER/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ARGB/@EntryIndexedValue">ARGB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LO/@EntryIndexedValue">LO</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RPC/@EntryIndexedValue">RPC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SDK/@EntryIndexedValue">SDK</s:String>
@@ -365,7 +365,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
Platform::CreateProcess(procSettings);
}
#endif
const bool distributionPackage = buildSettings->ForDistribution;
const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release;
{
CreateProcessSettings procSettings;
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
@@ -52,13 +52,18 @@ namespace FlaxEditor.CustomEditors
// Check if use provided editor
if (overrideEditor != null)
return overrideEditor;
ScriptType targetType = values.Type;
// Special case if property is a pure object type and all values are the same type
if (values.Type.Type == typeof(object) && values.Count > 0 && values[0] != null && !values.HasDifferentTypes)
if (targetType.Type == typeof(object) && values.Count > 0 && values[0] != null && !values.HasDifferentTypes)
return CreateEditor(TypeUtils.GetObjectType(values[0]), canUseRefPicker);
// Special case if property is interface but the value is implemented as Scripting Object that should use reference picker
if (targetType.IsInterface && canUseRefPicker && values.Count > 0 && values[0] is FlaxEngine.Object)
return new DummyEditor();
// Use editor for the property type
return CreateEditor(values.Type, canUseRefPicker);
return CreateEditor(targetType, canUseRefPicker);
}
internal static CustomEditor CreateEditor(ScriptType targetType, bool canUseRefPicker = true)
@@ -75,7 +75,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Selecting actor prefab asset
var selectPrefab = panel.Button("Select Prefab");
selectPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Select(prefab);
selectPrefab.Button.Clicked += () =>
{
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
Editor.Instance.Windows.ContentWin.Select(prefab);
};
// Viewing changes applied to this actor
var viewChanges = panel.Button("View Changes");
@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using FlaxEditor.Content.Settings;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -12,7 +13,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
[CustomEditor(typeof(LayersMask)), DefaultEditor]
internal class LayersMaskEditor : CustomEditor
{
private CheckBox[] _checkBoxes;
private List<CheckBox> _checkBoxes;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -24,16 +25,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
return;
}
_checkBoxes = new CheckBox[layers.Length];
_checkBoxes = new List<CheckBox>();
for (int i = 0; i < layers.Length; i++)
{
var layer = layers[i];
var property = layout.AddPropertyItem(layer);
if (string.IsNullOrEmpty(layer))
continue;
var property = layout.AddPropertyItem($"{i}: {layer}");
var checkbox = property.Checkbox().CheckBox;
UpdateCheckbox(checkbox, i);
checkbox.Tag = i;
checkbox.StateChanged += OnCheckboxStateChanged;
_checkBoxes[i] = checkbox;
_checkBoxes.Add(checkbox);
}
}
@@ -50,9 +53,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
if (_checkBoxes != null)
{
for (int i = 0; i < _checkBoxes.Length; i++)
for (int i = 0; i < _checkBoxes.Count; i++)
{
UpdateCheckbox(_checkBoxes[i], i);
UpdateCheckbox(_checkBoxes[i], (int)_checkBoxes[i].Tag);
}
}
@@ -456,14 +456,57 @@ namespace FlaxEditor.CustomEditors.Dedicated
for (int i = 0; i < layout.Children.Count; i++)
{
if (layout.Children[i] is GroupElement group && group.Panel.HeaderText == "Transform")
if (layout.Children[i] is GroupElement group && group.Panel.HeaderText.Equals("Transform", StringComparison.Ordinal))
{
VerticalPanelElement mainHor = VerticalPanelWithoutMargin(group);
CreateTransformElements(mainHor, ValuesTypes);
group.ContainerControl.ChangeChildIndex(mainHor.Control, 0);
layout.Children.Remove(group);
layout.ContainerControl.Children.Remove(group.Panel);
break;
}
}
// Setup transform
if (Presenter is LayoutElementsContainer l)
{
for (int i = 0; i < l.Children.Count; i++)
{
if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("Transform", StringComparison.Ordinal))
{
l.Children.Remove(g);
l.ContainerControl.Children.Remove(g.Panel);
break;
}
}
var transformGroup = l.Group("Transform");
VerticalPanelElement mainHor = VerticalPanelWithoutMargin(transformGroup);
CreateTransformElements(mainHor, ValuesTypes);
ScriptMemberInfo scaleInfo = ValuesTypes[0].GetProperty("Scale");
ItemInfo scaleItem = new ItemInfo(scaleInfo);
transformGroup.Property("Scale", scaleItem.GetValues(Values));
ScriptMemberInfo pivotInfo = ValuesTypes[0].GetProperty("Pivot");
ItemInfo pivotItem = new ItemInfo(pivotInfo);
transformGroup.Property("Pivot", pivotItem.GetValues(Values));
ScriptMemberInfo shearInfo = ValuesTypes[0].GetProperty("Shear");
ItemInfo shearItem = new ItemInfo(shearInfo);
transformGroup.Property("Shear", shearItem.GetValues(Values));
ScriptMemberInfo rotationInfo = ValuesTypes[0].GetProperty("Rotation");
ItemInfo rotationItem = new ItemInfo(rotationInfo);
transformGroup.Property("Rotation", rotationItem.GetValues(Values));
// Get position of general tab
for (int i = 0; i < l.Children.Count; i++)
{
if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("General", StringComparison.Ordinal) && i + 1 <= l.Children.Count)
{
Presenter.ContainerControl.ChangeChildIndex(transformGroup.Control, i + 1);
break;
}
}
}
}
private void CreateTransformElements(LayoutElementsContainer main, ScriptType[] valueTypes)
@@ -645,7 +688,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
var grid = UniformGridTwoByOne(el);
grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1);
var label = grid.Label(text);
var label = grid.Label(text, TextAlignment.Far);
var editor = grid.Object(values);
if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement)
{
@@ -57,17 +57,18 @@ namespace FlaxEditor.CustomEditors.Editors
menu.ItemsContainer.RemoveChildren();
menu.AddButton("Copy", linkedEditor.Copy);
var paste = menu.AddButton("Paste", linkedEditor.Paste);
paste.Enabled = linkedEditor.CanPaste;
var b = menu.AddButton("Paste", linkedEditor.Paste);
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
menu.AddSeparator();
var moveUpButton = menu.AddButton("Move up", OnMoveUpClicked);
moveUpButton.Enabled = Index > 0;
b = menu.AddButton("Move up", OnMoveUpClicked);
b.Enabled = Index > 0 && !Editor._readOnly;
var moveDownButton = menu.AddButton("Move down", OnMoveDownClicked);
moveDownButton.Enabled = Index + 1 < Editor.Count;
menu.AddButton("Remove", OnRemoveClicked);
b = menu.AddButton("Move down", OnMoveDownClicked);
b.Enabled = Index + 1 < Editor.Count && !Editor._readOnly;
b = menu.AddButton("Remove", OnRemoveClicked);
b.Enabled = !Editor._readOnly;
}
private void OnMoveUpClicked()
@@ -177,6 +178,7 @@ namespace FlaxEditor.CustomEditors.Editors
private IntValueBox _sizeBox;
private Color _background;
private int _elementsCount, _minCount, _maxCount;
private bool _readOnly;
private bool _canResize;
private bool _canReorderItems;
private CollectionAttribute.DisplayType _displayType;
@@ -209,6 +211,7 @@ namespace FlaxEditor.CustomEditors.Editors
return;
var size = Count;
_readOnly = false;
_canResize = true;
_canReorderItems = true;
_minCount = 0;
@@ -224,7 +227,8 @@ namespace FlaxEditor.CustomEditors.Editors
var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute);
if (collection != null)
{
_canResize = !collection.ReadOnly;
_canResize = collection.CanResize;
_readOnly = collection.ReadOnly;
_minCount = collection.MinCount;
_maxCount = collection.MaxCount;
_canReorderItems = collection.CanReorderItems;
@@ -235,6 +239,12 @@ namespace FlaxEditor.CustomEditors.Editors
spacing = collection.Spacing;
_displayType = collection.Display;
}
if (attributes != null && attributes.Any(x => x is ReadOnlyAttribute))
{
_readOnly = true;
_canResize = false;
_canReorderItems = false;
}
if (_maxCount == 0)
_maxCount = ushort.MaxValue;
_canResize &= _minCount < _maxCount;
@@ -243,8 +253,7 @@ namespace FlaxEditor.CustomEditors.Editors
dragArea.CustomControl.Editor = this;
dragArea.CustomControl.ElementType = ElementType;
// Check for the AssetReferenceAttribute. In JSON assets, it can be used to filter
// which scripts can be dragged over and dropped on this collection editor.
// Check for the AssetReferenceAttribute. In JSON assets, it can be used to filter which scripts can be dragged over and dropped on this collection editor
var assetReference = (AssetReferenceAttribute)attributes?.FirstOrDefault(x => x is AssetReferenceAttribute);
if (assetReference != null)
{
@@ -333,6 +342,8 @@ namespace FlaxEditor.CustomEditors.Editors
var property = panel.AddPropertyItem(itemLabel);
var itemLayout = (LayoutElementsContainer)property;
itemLabel.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor);
if (_readOnly && itemLayout.Children.Count > 0)
GenericEditor.OnReadOnlyProperty(itemLayout);
}
else if (_displayType == CollectionAttribute.DisplayType.Header || (_displayType == CollectionAttribute.DisplayType.Default && !single))
{
@@ -340,13 +351,15 @@ namespace FlaxEditor.CustomEditors.Editors
cdp.CustomControl.Setup(this, i, _canReorderItems);
var itemLayout = cdp.VerticalPanel();
cdp.CustomControl.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor);
if (_readOnly && itemLayout.Children.Count > 0)
GenericEditor.OnReadOnlyProperty(itemLayout);
}
}
}
_elementsCount = size;
// Add/Remove buttons
if (_canResize)
if (_canResize && !_readOnly)
{
var panel = dragArea.HorizontalPanel();
panel.Panel.Size = new Float2(0, 20);
@@ -131,7 +131,7 @@ namespace FlaxEditor.CustomEditors.Editors
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
if (button == MouseButton.Left)
if (button == MouseButton.Left && _editor._canEditKeys)
{
OnEditClicked(null);
return true;
@@ -189,6 +189,7 @@ namespace FlaxEditor.CustomEditors.Editors
var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute);
if (collection != null)
{
_canEditKeys &= collection.CanReorderItems;
_readOnly = collection.ReadOnly;
_notNullItems = collection.NotNullItems;
if (collection.BackgroundColor.HasValue)
@@ -197,6 +198,11 @@ namespace FlaxEditor.CustomEditors.Editors
spacing = collection.Spacing;
_displayType = collection.Display;
}
if (attributes != null && attributes.Any(x => x is ReadOnlyAttribute))
{
_readOnly = true;
_canEditKeys = false;
}
// Size
if (layout.ContainerControl is DropPanel dropPanel)
@@ -239,14 +245,6 @@ namespace FlaxEditor.CustomEditors.Editors
var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType<object>();
var keys = keysEnumerable as object[] ?? keysEnumerable.ToArray();
var valuesType = new ScriptType(valueType);
bool single = valuesType.IsPrimitive ||
valuesType.Equals(new ScriptType(typeof(string))) ||
valuesType.IsEnum ||
(valuesType.GetFields().Length == 1 && valuesType.GetProperties().Length == 0) ||
(valuesType.GetProperties().Length == 1 && valuesType.GetFields().Length == 0) ||
valuesType.Equals(new ScriptType(typeof(JsonAsset))) ||
valuesType.Equals(new ScriptType(typeof(SettingsBase)));
// Use separate layout cells for each collection items to improve layout updates for them in separation
var useSharedLayout = valueType.IsPrimitive || valueType.IsEnum;
@@ -263,6 +261,8 @@ namespace FlaxEditor.CustomEditors.Editors
var property = panel.AddPropertyItem(new DictionaryItemLabel(this, key));
var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel();
itemLayout.Object(new DictionaryValueContainer(valuesType, key, Values), overrideEditor);
if (_readOnly && itemLayout.Children.Count > 0)
GenericEditor.OnReadOnlyProperty(itemLayout);
}
}
_elementsCount = size;
@@ -0,0 +1,17 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
namespace FlaxEditor.CustomEditors.Editors
{
internal sealed class DummyEditor : CustomEditor
{
public override void Initialize(LayoutElementsContainer layout)
{
string valueName;
if (Values.Count != 0 && Values[0] != null)
valueName = Values[0].ToString();
else
valueName = "null";
layout.Label($"{valueName} ({Values.Type})");
}
}
}
@@ -581,6 +581,43 @@ namespace FlaxEditor.CustomEditors.Editors
return layout;
}
internal static void OnReadOnlyProperty(LayoutElementsContainer itemLayout, int labelIndex = -1)
{
PropertiesListElement list = null;
int firstChildControlIndex = 0;
bool disableSingle = true;
var control = itemLayout.Children[itemLayout.Children.Count - 1];
if (control is GroupElement group && group.Children.Count > 0)
{
list = group.Children[0] as PropertiesListElement;
disableSingle = false; // Disable all nested editors
}
else if (control is PropertiesListElement list1 && labelIndex != -1)
{
list = list1;
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
}
else if (control?.Control != null)
{
control.Control.Enabled = false;
}
if (list != null)
{
// Disable controls added to the editor
var count = list.Properties.Children.Count;
for (int j = firstChildControlIndex; j < count; j++)
{
var child = list.Properties.Children[j];
if (disableSingle && child is PropertyNameLabel)
break;
if (child != null)
child.Enabled = false;
}
}
}
/// <summary>
/// Evaluate the <see cref="VisibleIfAttribute"/> cache for a given property item.
/// </summary>
@@ -660,35 +697,7 @@ namespace FlaxEditor.CustomEditors.Editors
if (item.IsReadOnly && itemLayout.Children.Count > 0)
{
PropertiesListElement list = null;
int firstChildControlIndex = 0;
bool disableSingle = true;
var control = itemLayout.Children[itemLayout.Children.Count - 1];
if (control is GroupElement group && group.Children.Count > 0)
{
list = group.Children[0] as PropertiesListElement;
disableSingle = false; // Disable all nested editors
}
else if (control is PropertiesListElement list1)
{
list = list1;
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
}
if (list != null)
{
// Disable controls added to the editor
var count = list.Properties.Children.Count;
for (int j = firstChildControlIndex; j < count; j++)
{
var child = list.Properties.Children[j];
if (disableSingle && child is PropertyNameLabel)
break;
if (child != null)
child.Enabled = false;
}
}
OnReadOnlyProperty(itemLayout, labelIndex);
}
EvaluateVisibleIf(itemLayout, item, labelIndex);
@@ -13,6 +13,9 @@ namespace FlaxEditor.CustomEditors.Editors
public sealed class StringEditor : CustomEditor
{
private TextBoxElement _element;
private string _watermarkText;
private Color _watermarkColor;
private Color _defaultWatermarkColor;
/// <inheritdoc />
public override DisplayStyle Style => DisplayStyle.Inline;
@@ -21,15 +24,26 @@ namespace FlaxEditor.CustomEditors.Editors
public override void Initialize(LayoutElementsContainer layout)
{
bool isMultiLine = false;
_watermarkText = string.Empty;
var attributes = Values.GetAttributes();
var multiLine = attributes?.FirstOrDefault(x => x is MultilineTextAttribute);
var watermarkAttribute = attributes?.FirstOrDefault(x => x is WatermarkAttribute);
if (multiLine != null)
{
isMultiLine = true;
}
_element = layout.TextBox(isMultiLine);
_defaultWatermarkColor = _element.TextBox.WatermarkTextColor;
if (watermarkAttribute is WatermarkAttribute watermark)
{
_watermarkText = watermark.WatermarkText;
var watermarkColor = watermark.WatermarkColor > 0 ? Color.FromRGB(watermark.WatermarkColor) : FlaxEngine.GUI.Style.Current.ForegroundDisabled;
_watermarkColor = watermarkColor;
_element.TextBox.WatermarkText = watermark.WatermarkText;
_element.TextBox.WatermarkTextColor = watermarkColor;
}
_element.TextBox.EditEnd += () => SetValue(_element.Text);
}
@@ -41,12 +55,14 @@ namespace FlaxEditor.CustomEditors.Editors
if (HasDifferentValues)
{
_element.TextBox.Text = string.Empty;
_element.TextBox.WatermarkTextColor = _defaultWatermarkColor;
_element.TextBox.WatermarkText = "Different values";
}
else
{
_element.TextBox.Text = (string)Values[0];
_element.TextBox.WatermarkText = string.Empty;
_element.TextBox.WatermarkTextColor = _watermarkColor;
_element.TextBox.WatermarkText = _watermarkText;
}
}
}
@@ -242,7 +242,7 @@ namespace FlaxEditor.CustomEditors.GUI
float namesWidth = _splitterValue * Width;
int count = _element.Labels.Count;
float[] yStarts = new float[count + 1];
for (int i = 1; i < count; i++)
for (int i = 0; i < count; i++)
{
var label = _element.Labels[i];
@@ -251,9 +251,13 @@ namespace FlaxEditor.CustomEditors.GUI
else if (_children.Count <= label.FirstChildControlIndex)
yStarts[i] = y;
else
{
yStarts[i] = _children[label.FirstChildControlIndex].Top;
if (i == count - 1)
yStarts[i + 1] = _children[label.FirstChildControlIndex].Bottom;
}
}
yStarts[count] = y;
for (int i = 0; i < count; i++)
{
var label = _element.Labels[i];
@@ -318,7 +318,7 @@ namespace FlaxEditor.CustomEditors
if (header.FontSize > 0)
element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize);
if (header.Color > 0)
element.Label.TextColor = Color.FromRGBA(header.Color);
element.Label.TextColor = Color.FromRGB(header.Color);
return element;
}
+21 -3
View File
@@ -264,6 +264,7 @@ namespace FlaxEditor.GUI.Dialogs
{
Text = "+",
Parent = this,
TooltipText = "Save Color.",
Tag = null,
};
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
@@ -370,9 +371,25 @@ namespace FlaxEditor.GUI.Dialogs
Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center);
// Color difference
var newRect = new Rectangle(_cOK.X, _cHex.Bottom + PickerMargin, _cCancel.Right - _cOK.Left, 0);
newRect.Size.Y = _cValue.Bottom - newRect.Y;
Render2D.FillRectangle(newRect, _value * _value.A);
var newRect = new Rectangle(_cOK.X - 3, _cHex.Bottom + PickerMargin, 130, 0);
newRect.Size.Y = 50;
Render2D.FillRectangle(newRect, Color.White);
var smallRectSize = 10;
var numHor = Mathf.FloorToInt(newRect.Width / smallRectSize);
var numVer = Mathf.FloorToInt(newRect.Height / smallRectSize);
// Draw checkerboard for background of color to help with transparency
for (int i = 0; i < numHor; i++)
{
for (int j = 0; j < numVer; j++)
{
if ((i + j) % 2 == 0 )
{
var rect = new Rectangle(newRect.X + smallRectSize * i, newRect.Y + smallRectSize * j, new Float2(smallRectSize));
Render2D.FillRectangle(rect, Color.Gray);
}
}
}
Render2D.FillRectangle(newRect, _value);
}
/// <inheritdoc />
@@ -498,6 +515,7 @@ namespace FlaxEditor.GUI.Dialogs
{
Text = "+",
Parent = this,
TooltipText = "Save Color.",
Tag = null,
};
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
+3 -1
View File
@@ -311,7 +311,9 @@ namespace FlaxEditor.GUI.Dialogs
// Alpha
float alphaY = _slider2Rect.Height * (1 - _color.A);
var alphaR = new Rectangle(_slider2Rect.X - slidersOffset, _slider2Rect.Y + alphaY - slidersThickness / 2, _slider2Rect.Width + slidersOffset * 2, slidersThickness);
Render2D.FillRectangle(_slider2Rect, _color, _color, Color.Transparent, Color.Transparent);
var color = _color;
color.A = 1; // Keep slider 2 fill rect from changing color alpha while selecting.
Render2D.FillRectangle(_slider2Rect, color, color, Color.Transparent, Color.Transparent);
Render2D.DrawRectangle(_slider2Rect, _isMouseDownSlider2 ? style.BackgroundSelected : Color.Black);
Render2D.DrawRectangle(alphaR, _isMouseDownSlider2 ? Color.White : Color.Gray);
}
+107
View File
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using FlaxEditor.SceneGraph;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Utilities;
namespace FlaxEditor.GUI.Drag;
/// <summary>
/// Control type drag handler.
/// </summary>
public sealed class DragControlType : DragActorType<DragEventArgs>
{
/// <summary>
/// Initializes a new instance of the <see cref="DragControlType"/> class.
/// </summary>
/// <param name="validateFunction">The validation function</param>
public DragControlType(Func<ScriptType, bool> validateFunction)
: base(validateFunction)
{
}
}
/// <summary>
/// Helper class for handling control type drag and drop (for spawning).
/// </summary>
/// <seealso cref="Control" />
/// <seealso cref="ActorNode" />
public class DragControlType<U> : DragHelper<ScriptType, U> where U : DragEventArgs
{
/// <summary>
/// The default prefix for drag data used for actor type drag and drop.
/// </summary>
public const string DragPrefix = "CTYPE!?";
/// <summary>
/// Creates a new DragHelper
/// </summary>
/// <param name="validateFunction">The validation function</param>
public DragControlType(Func<ScriptType, bool> validateFunction)
: base(validateFunction)
{
}
/// <inheritdoc/>
public override DragData ToDragData(ScriptType item) => GetDragData(item);
/// <inheritdoc/>
public override DragData ToDragData(IEnumerable<ScriptType> items)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the drag data.
/// </summary>
/// <param name="item">The control type.</param>
/// <returns>The data</returns>
public static DragData GetDragData(Type item)
{
if (item == null)
throw new ArgumentNullException();
return new DragDataText(DragPrefix + item.FullName);
}
/// <summary>
/// Gets the drag data.
/// </summary>
/// <param name="item">The control type.</param>
/// <returns>The data</returns>
public static DragData GetDragData(ScriptType item)
{
if (item == ScriptType.Null)
throw new ArgumentNullException();
return new DragDataText(DragPrefix + item.TypeName);
}
/// <summary>
/// Tries to parse the drag data to extract <see cref="Type"/> collection.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>Gathered objects or empty array if cannot get any valid.</returns>
public override IEnumerable<ScriptType> FromDragData(DragData data)
{
if (data is DragDataText dataText)
{
if (dataText.Text.StartsWith(DragPrefix))
{
// Remove prefix and parse spitted names
var types = dataText.Text.Remove(0, DragPrefix.Length).Split('\n');
var results = new List<ScriptType>(types.Length);
for (int i = 0; i < types.Length; i++)
{
// Find type
var obj = TypeUtils.GetType(types[i]);
if (obj)
results.Add(obj);
}
return results;
}
}
return Utils.GetEmptyArray<ScriptType>();
}
}
+10
View File
@@ -418,9 +418,19 @@ namespace FlaxEditor.GUI.Tabs
{
// If scroll bar is visible it covers part of the tab header so include this in tab size to improve usability
if (_orientation == Orientation.Horizontal && TabsPanel.HScrollBar.Visible)
{
tabsSize.Y += TabsPanel.HScrollBar.Height;
var style = Style.Current;
TabsPanel.HScrollBar.TrackColor = style.Background;
TabsPanel.HScrollBar.ThumbColor = style.ForegroundGrey;
}
else if (_orientation == Orientation.Vertical && TabsPanel.VScrollBar.Visible)
{
tabsSize.X += TabsPanel.VScrollBar.Width;
var style = Style.Current;
TabsPanel.VScrollBar.TrackColor = style.Background;
TabsPanel.VScrollBar.ThumbColor = style.ForegroundGrey;
}
}
// Fit the tabs panel
@@ -130,7 +130,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
/// The keyframes.
/// </summary>
[EditorDisplay("Keyframes", EditorDisplayAttribute.InlineStyle), ExpandGroups]
[Collection(CanReorderItems = false, ReadOnly = true)]
[Collection(CanReorderItems = false, CanResize = true)]
public List<KeyValuePair<string, object>> Keyframes;
/// <inheritdoc />
@@ -159,7 +159,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
/// The parameters values.
/// </summary>
[EditorDisplay("Parameters", EditorDisplayAttribute.InlineStyle), ExpandGroups]
[Collection(CanReorderItems = false, ReadOnly = true)]
[Collection(CanReorderItems = false, CanResize = true)]
public object[] Parameters;
/// <inheritdoc />
+48 -15
View File
@@ -698,6 +698,38 @@ namespace FlaxEditor.GUI.Tree
}
}
// Show tree guide lines
if (Editor.Instance.Options.Options.Interface.ShowTreeLines)
{
TreeNode parentNode = Parent as TreeNode;
bool thisNodeIsLast = false;
while (parentNode != null && parentNode != ParentTree.Children[0])
{
float bottomOffset = 0;
float topOffset = 0;
if (Parent == parentNode && this == Parent.Children[0])
topOffset = 2;
if (thisNodeIsLast && parentNode.Children.Count == 1)
bottomOffset = topOffset != 0 ? 4 : 2;
if (Parent == parentNode && this == Parent.Children[Parent.Children.Count - 1] && !_opened)
{
thisNodeIsLast = true;
bottomOffset = topOffset != 0 ? 4 : 2;
}
float leftOffset = 9;
// Adjust offset for icon image
if (_iconCollaped.IsValid)
leftOffset += 18;
var lineRect1 = new Rectangle(parentNode.TextRect.Left - leftOffset, parentNode.HeaderRect.Top + topOffset, 1, parentNode.HeaderRect.Height - bottomOffset);
Render2D.FillRectangle(lineRect1, isSelected ? style.ForegroundGrey : style.LightBackground);
parentNode = parentNode.Parent as TreeNode;
}
}
// Base
if (_opened)
{
@@ -729,13 +761,16 @@ namespace FlaxEditor.GUI.Tree
// Try to estimate the rough location of the first node, assuming the node height is constant
var firstChildGlobalRect = GetChildGlobalRectangle(children[0], ref globalTransform);
var firstVisibleChild = Math.Clamp((int)Math.Floor((globalClipping.Y - firstChildGlobalRect.Top) / firstChildGlobalRect.Height) + 1, 0, children.Count - 1);
if (GetChildGlobalRectangle(children[firstVisibleChild], ref globalTransform).Top > globalClipping.Top)
var firstVisibleChild = Math.Clamp((int)Math.Floor((globalClipping.Y - firstChildGlobalRect.Top) / _headerHeight) + 1, 0, children.Count - 1);
if (GetChildGlobalRectangle(children[firstVisibleChild], ref globalTransform).Top > globalClipping.Top || !children[firstVisibleChild].Visible)
{
// Overshoot...
// Estimate overshoot, either it's partially visible or hidden in the tree
for (; firstVisibleChild > 0; firstVisibleChild--)
{
var child = children[firstVisibleChild];
if (!child.Visible)
continue;
if (GetChildGlobalRectangle(child, ref globalTransform).Top < globalClipping.Top)
break;
}
@@ -744,18 +779,16 @@ namespace FlaxEditor.GUI.Tree
for (int i = firstVisibleChild; i < children.Count; i++)
{
var child = children[i];
if (child.Visible)
{
var childGlobalRect = GetChildGlobalRectangle(child, ref globalTransform);
if (globalClipping.Intersects(ref childGlobalRect))
{
Render2D.PushTransform(ref child._cachedTransform);
child.Draw();
Render2D.PopTransform();
}
else
break;
}
if (!child.Visible)
continue;
var childGlobalRect = GetChildGlobalRectangle(child, ref globalTransform);
if (!globalClipping.Intersects(ref childGlobalRect))
break;
Render2D.PushTransform(ref child._cachedTransform);
child.Draw();
Render2D.PopTransform();
}
static Rectangle GetChildGlobalRectangle(Control control, ref Matrix3x3 globalTransform)
+29 -25
View File
@@ -253,7 +253,11 @@ namespace FlaxEditor
{
// Select node (with additive mode)
var selection = new List<SceneGraphNode>();
if (Root.GetKey(KeyboardKeys.Control))
if (Root.GetKey(KeyboardKeys.Shift) && transformGizmo.Selection.Contains(uiControlNode))
{
// Move whole selection
}
else if (Root.GetKey(KeyboardKeys.Control))
{
// Add/remove from selection
selection.AddRange(transformGizmo.Selection);
@@ -261,13 +265,14 @@ namespace FlaxEditor
selection.Remove(uiControlNode);
else
selection.Add(uiControlNode);
owner.Select(selection);
}
else
{
// Select
selection.Add(uiControlNode);
owner.Select(selection);
}
owner.Select(selection);
// Initialize control movement
_mouseMovesControl = true;
@@ -499,6 +504,15 @@ namespace FlaxEditor
bool drawAnySelectedControl = false;
var transformGizmo = TransformGizmo;
var mousePos = PointFromWindow(RootWindow.MousePosition);
if (EnableSelecting && !_mouseMovesControl && !_mouseMovesWidget && IsMouseOver)
{
// Highlight control under mouse for easier selecting (except if already selected)
if (RayCastControl(ref mousePos, out var hitControl) &&
(transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl)))
{
DrawControl(null, hitControl, false, ref mousePos, ref drawAnySelectedControl);
}
}
if (transformGizmo != null)
{
// Selected UI controls outline
@@ -511,15 +525,6 @@ namespace FlaxEditor
}
}
}
if (EnableSelecting && !_mouseMovesControl && !_mouseMovesWidget && IsMouseOver)
{
// Highlight control under mouse for easier selecting (except if already selected)
if (RayCastControl(ref mousePos, out var hitControl) &&
(transformGizmo == null || !transformGizmo.Selection.Any(x => x.EditableObject is UIControl controlActor && controlActor.Control == hitControl)))
{
DrawControl(null, hitControl, false, ref mousePos, ref drawAnySelectedControl);
}
}
if (drawAnySelectedControl)
Render2D.PopTransform();
@@ -617,40 +622,39 @@ namespace FlaxEditor
// Draw sizing widgets
if (_widgets == null)
_widgets = new List<Widget>();
var widgetSize = 8.0f;
var widgetSize = 10.0f;
var viewScale = ViewScale;
if (viewScale < 0.7f)
widgetSize *= viewScale;
var controlSize = control.Size.Absolute.MinValue / 50.0f;
if (controlSize < 1.0f)
widgetSize *= Mathf.Clamp(controlSize + 0.1f, 0.1f, 1.0f);
var cornerSize = new Float2(widgetSize);
DrawControlWidget(uiControl, ref ul, ref mousePos, ref cornerSize, new Float2(-1, -1), CursorType.SizeNWSE);
DrawControlWidget(uiControl, ref ur, ref mousePos, ref cornerSize, new Float2(1, -1), CursorType.SizeNESW);
DrawControlWidget(uiControl, ref bl, ref mousePos, ref cornerSize, new Float2(-1, 1), CursorType.SizeNESW);
DrawControlWidget(uiControl, ref br, ref mousePos, ref cornerSize, new Float2(1, 1), CursorType.SizeNWSE);
var edgeSizeV = new Float2(widgetSize * 2, widgetSize);
var edgeSizeH = new Float2(edgeSizeV.Y, edgeSizeV.X);
var widgetHandleSize = new Float2(widgetSize);
DrawControlWidget(uiControl, ref ul, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, -1), CursorType.SizeNWSE);
DrawControlWidget(uiControl, ref ur, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, -1), CursorType.SizeNESW);
DrawControlWidget(uiControl, ref bl, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, 1), CursorType.SizeNESW);
DrawControlWidget(uiControl, ref br, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, 1), CursorType.SizeNWSE);
Float2.Lerp(ref ul, ref bl, 0.5f, out var el);
Float2.Lerp(ref ur, ref br, 0.5f, out var er);
Float2.Lerp(ref ul, ref ur, 0.5f, out var eu);
Float2.Lerp(ref bl, ref br, 0.5f, out var eb);
DrawControlWidget(uiControl, ref el, ref mousePos, ref edgeSizeH, new Float2(-1, 0), CursorType.SizeWE);
DrawControlWidget(uiControl, ref er, ref mousePos, ref edgeSizeH, new Float2(1, 0), CursorType.SizeWE);
DrawControlWidget(uiControl, ref eu, ref mousePos, ref edgeSizeV, new Float2(0, -1), CursorType.SizeNS);
DrawControlWidget(uiControl, ref eb, ref mousePos, ref edgeSizeV, new Float2(0, 1), CursorType.SizeNS);
DrawControlWidget(uiControl, ref el, ref mousePos, ref widgetHandleSize, viewScale, new Float2(-1, 0), CursorType.SizeWE);
DrawControlWidget(uiControl, ref er, ref mousePos, ref widgetHandleSize, viewScale, new Float2(1, 0), CursorType.SizeWE);
DrawControlWidget(uiControl, ref eu, ref mousePos, ref widgetHandleSize, viewScale, new Float2(0, -1), CursorType.SizeNS);
DrawControlWidget(uiControl, ref eb, ref mousePos, ref widgetHandleSize, viewScale, new Float2(0, 1), CursorType.SizeNS);
// TODO: draw anchors
}
}
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, Float2 resizeAxis, CursorType cursor)
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size,float scale, Float2 resizeAxis, CursorType cursor)
{
var style = Style.Current;
var rect = new Rectangle(pos - size * 0.5f, size);
var rect = new Rectangle((pos + resizeAxis * 10 * scale) - size * 0.5f, size);
if (rect.Contains(ref mousePos))
{
Render2D.FillRectangle(rect, style.Foreground);
Render2D.DrawRectangle(rect, style.SelectionBorder);
}
else
{
+1
View File
@@ -202,6 +202,7 @@ namespace FlaxEditor.Modules
var prefabId = ((ActorNode)selection[0]).Actor.PrefabID;
var prefab = FlaxEngine.Content.LoadAsync<Prefab>(prefabId);
Editor.Windows.ContentWin.ClearItemsSearch();
Editor.Windows.ContentWin.Select(prefab);
}
+2 -2
View File
@@ -565,6 +565,7 @@ namespace FlaxEditor.Modules
if (item != null)
Editor.ContentEditing.Open(item);
});
cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show());
// Scene
MenuScene = MainMenu.AddButton("Scene");
@@ -619,7 +620,6 @@ namespace FlaxEditor.Modules
_menuToolsTakeScreenshot = cm.AddButton("Take screenshot", inputOptions.TakeScreenshot, Editor.Windows.TakeScreenshot);
cm.AddSeparator();
cm.AddButton("Plugins", () => Editor.Windows.PluginsWin.Show());
cm.AddButton("Options", () => Editor.Windows.EditorOptionsWin.Show());
// Window
MenuWindow = MainMenu.AddButton("Window");
@@ -640,7 +640,7 @@ namespace FlaxEditor.Modules
cm.AddButton("Visual Script Debugger", Editor.Windows.VisualScriptDebuggerWin.FocusOrShow);
cm.AddSeparator();
cm.AddButton("Save window layout", Editor.Windows.SaveLayout);
_menuWindowApplyWindowLayout = cm.AddChildMenu("Apply window layout");
_menuWindowApplyWindowLayout = cm.AddChildMenu("Window layouts");
cm.AddButton("Restore default layout", Editor.Windows.LoadDefaultLayout);
// Help
@@ -210,6 +210,13 @@ namespace FlaxEditor.Options
[EditorDisplay("Interface"), EditorOrder(310)]
public bool SeparateValueAndUnit { get; set; }
/// <summary>
/// Gets or sets the option to put a space between numbers and units for unit formatting.
/// </summary>
[DefaultValue(true)]
[EditorDisplay("Interface"), EditorOrder(320)]
public bool ShowTreeLines { get; set; } = true;
/// <summary>
/// Gets or sets the timestamps prefix mode for output log messages.
/// </summary>
+11
View File
@@ -255,6 +255,17 @@ namespace FlaxEditor.Options
}
}
// Ensure custom fonts are valid, reset if not
var defaultInterfaceOptions = new InterfaceOptions();
if (Style.Current.FontTitle == null)
Style.Current.FontTitle = defaultInterfaceOptions.TitleFont.GetFont();
if (Style.Current.FontSmall == null)
Style.Current.FontSmall = defaultInterfaceOptions.SmallFont.GetFont();
if (Style.Current.FontMedium == null)
Style.Current.FontMedium = defaultInterfaceOptions.MediumFont.GetFont();
if (Style.Current.FontLarge == null)
Style.Current.FontLarge = defaultInterfaceOptions.LargeFont.GetFont();
// Set fallback fonts
var fallbackFonts = Options.Interface.FallbackFonts;
if (fallbackFonts == null || fallbackFonts.Length == 0 || fallbackFonts.All(x => x == null))
+62 -7
View File
@@ -15,6 +15,7 @@ using FlaxEditor.Windows.Assets;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Utilities;
using Object = FlaxEngine.Object;
namespace FlaxEditor.SceneGraph.GUI
{
@@ -29,6 +30,7 @@ namespace FlaxEditor.SceneGraph.GUI
private DragScripts _dragScripts;
private DragAssets _dragAssets;
private DragActorType _dragActorType;
private DragControlType _dragControlType;
private DragScriptItems _dragScriptItems;
private DragHandlers _dragHandlers;
private List<Rectangle> _highlights;
@@ -354,7 +356,7 @@ namespace FlaxEditor.SceneGraph.GUI
private void OnRenamed(RenamePopup renamePopup)
{
using (new UndoBlock(ActorNode.Root.Undo, Actor, "Rename"))
Actor.Name = renamePopup.Text;
Actor.Name = renamePopup.Text.Trim();
}
/// <inheritdoc />
@@ -439,6 +441,17 @@ namespace FlaxEditor.SceneGraph.GUI
}
if (_dragActorType.OnDragEnter(data))
return _dragActorType.Effect;
// Check if drag control type
if (_dragControlType == null)
{
_dragControlType = new DragControlType(ValidateDragControlType);
_dragHandlers.Add(_dragControlType);
}
if (_dragControlType.OnDragEnter(data))
return _dragControlType.Effect;
// Check if drag script item
if (_dragScriptItems == null)
{
_dragScriptItems = new DragScriptItems(ValidateDragScriptItem);
@@ -572,10 +585,33 @@ namespace FlaxEditor.SceneGraph.GUI
Editor.LogWarning("Failed to spawn actor of type " + item.TypeName);
continue;
}
actor.StaticFlags = Actor.StaticFlags;
actor.StaticFlags = newParent.StaticFlags;
actor.Name = item.Name;
actor.Transform = Actor.Transform;
ActorNode.Root.Spawn(actor, Actor);
ActorNode.Root.Spawn(actor, newParent);
actor.OrderInParent = newOrder;
}
result = DragDropEffect.Move;
}
// Drag control type
else if (_dragControlType != null && _dragControlType.HasValidDrag)
{
for (int i = 0; i < _dragControlType.Objects.Count; i++)
{
var item = _dragControlType.Objects[i];
var control = item.CreateInstance() as Control;
if (control == null)
{
Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName);
continue;
}
var uiControl = new UIControl
{
Control = control,
StaticFlags = newParent.StaticFlags,
Name = item.Name,
};
ActorNode.Root.Spawn(uiControl, newParent);
uiControl.OrderInParent = newOrder;
}
result = DragDropEffect.Move;
}
@@ -590,6 +626,7 @@ namespace FlaxEditor.SceneGraph.GUI
{
var item = _dragScriptItems.Objects[i];
var actorType = Editor.Instance.CodeEditing.Actors.Get(item);
var scriptType = Editor.Instance.CodeEditing.Scripts.Get(item);
if (actorType != ScriptType.Null)
{
var actor = actorType.CreateInstance() as Actor;
@@ -604,6 +641,18 @@ namespace FlaxEditor.SceneGraph.GUI
ActorNode.Root.Spawn(actor, spawnParent);
actor.OrderInParent = newOrder;
}
else if (scriptType != ScriptType.Null)
{
if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below)
{
Editor.LogWarning("Failed to spawn script of type " + actorType.TypeName);
continue;
}
IUndoAction action = new AddRemoveScript(true, newParent, scriptType);
Select();
ActorNode.Root.Undo?.AddAction(action);
action.Do();
}
}
result = DragDropEffect.Move;
}
@@ -656,12 +705,17 @@ namespace FlaxEditor.SceneGraph.GUI
private static bool ValidateDragActorType(ScriptType actorType)
{
return true;
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
}
private static bool ValidateDragScriptItem(ScriptItem script)
private static bool ValidateDragControlType(ScriptType controlType)
{
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
}
private bool ValidateDragScriptItem(ScriptItem script)
{
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null || Editor.Instance.CodeEditing.Scripts.Get(script) != ScriptType.Null;
}
/// <inheritdoc />
@@ -704,6 +758,7 @@ namespace FlaxEditor.SceneGraph.GUI
_dragScripts = null;
_dragAssets = null;
_dragActorType = null;
_dragControlType = null;
_dragScriptItems = null;
_dragHandlers?.Clear();
_dragHandlers = null;
@@ -186,6 +186,18 @@ namespace FlaxEditor.Surface.Archetypes
base.OnEndMouseCapture();
}
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
switch (key)
{
case KeyboardKeys.Delete:
_editor.SetAsset(_index, Guid.Empty);
return true;
}
return base.OnKeyDown(key);
}
}
/// <summary>
+1 -1
View File
@@ -430,7 +430,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Smoothstep",
Description = "Returns a smooth Hermite interpolation between 0 and 1, if value is in the range [min, max].",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(120, 60),
Size = new Float2(200, 60),
ConnectionsHints = ConnectionsHint.Numeric,
IndependentBoxes = new[] { 0, 1, 2 },
DependentBoxes = new[] { 3 },
@@ -66,6 +66,8 @@ namespace FlaxEditor.Surface.Undo
// Initialize
if (node.Values != null && node.Values.Length == _nodeValues.Length)
Array.Copy(_nodeValues, node.Values, _nodeValues.Length);
else if (_nodeValues != null && (node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
node.Values = (object[])_nodeValues.Clone();
else if (_nodeValues != null && _nodeValues.Length != 0)
throw new InvalidOperationException("Invalid node values.");
node.Location = _nodeLocation;
@@ -82,7 +82,7 @@ namespace FlaxEditor.Tools.Foliage
}
}
[EditorOrder(20), EditorDisplay("Model"), Collection(ReadOnly = true), Tooltip("Model materials override collection. Can be used to change a specific material of the mesh to the custom one without editing the asset.")]
[EditorOrder(20), EditorDisplay("Model"), Collection(CanResize = true), Tooltip("Model materials override collection. Can be used to change a specific material of the mesh to the custom one without editing the asset.")]
public MaterialBase[] Materials
{
get
+2 -2
View File
@@ -155,8 +155,8 @@ namespace FlaxEditor.Utilities
var scene = Level.LoadSceneFromBytes(data.Bytes);
if (scene == null)
{
Profiler.EndEvent();
throw new Exception("Failed to deserialize scene");
Editor.LogError("Failed to restore scene");
continue;
}
// Restore `dirty` state
@@ -351,6 +351,8 @@ namespace FlaxEditor.Viewport
private void OnCollectDrawCalls(ref RenderContext renderContext)
{
if (renderContext.View.Pass == DrawPass.Depth)
return;
DragHandlers.CollectDrawCalls(_debugDrawData, ref renderContext);
if (ShowNavigation)
Editor.Internal_DrawNavMesh();
@@ -620,12 +622,12 @@ namespace FlaxEditor.Viewport
private static bool ValidateDragActorType(ScriptType actorType)
{
return Level.IsAnySceneLoaded;
return Level.IsAnySceneLoaded && Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
}
private static bool ValidateDragScriptItem(ScriptItem script)
{
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
return Level.IsAnySceneLoaded && Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
}
/// <inheritdoc />
@@ -98,7 +98,6 @@ namespace FlaxEditor.Viewport
ShowDebugDraw = true;
ShowEditorPrimitives = true;
Gizmos = new GizmosCollection(this);
var inputOptions = window.Editor.Options.Options.Input;
// Prepare rendering task
Task.ActorsSource = ActorsSources.CustomActors;
@@ -219,6 +218,8 @@ namespace FlaxEditor.Viewport
private void OnCollectDrawCalls(ref RenderContext renderContext)
{
if (renderContext.View.Pass == DrawPass.Depth)
return;
DragHandlers.CollectDrawCalls(_debugDrawData, ref renderContext);
_debugDrawData.OnDraw(ref renderContext);
}
@@ -498,7 +499,7 @@ namespace FlaxEditor.Viewport
private static bool ValidateDragActorType(ScriptType actorType)
{
return true;
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
}
private static bool ValidateDragScriptItem(ScriptItem script)
@@ -302,8 +302,17 @@ namespace FlaxEditor.Windows.Assets
// TODO: improve the UI
layout.Space(40);
var addParamType = layout.ComboBox().ComboBox;
addParamType.Items = AllowedTypes.Select(CustomEditorsUtil.GetTypeNameUI).ToList();
addParamType.SelectedIndex = 0;
object lastValue = null;
foreach (var e in _proxy.DefaultValues)
lastValue = e.Value;
var allowedTypes = AllowedTypes.Select(CustomEditorsUtil.GetTypeNameUI).ToList();
int index = 0;
if (lastValue != null)
index = allowedTypes.FindIndex(x => x.Equals(CustomEditorsUtil.GetTypeNameUI(lastValue.GetType()), StringComparison.Ordinal));
addParamType.Items = allowedTypes;
addParamType.SelectedIndex = index;
_addParamType = addParamType;
var addParamButton = layout.Button("Add").Button;
addParamButton.Clicked += OnAddParamButtonClicked;
@@ -65,6 +65,7 @@ namespace FlaxEditor.Windows.Assets
private PrefabWindow _window;
private DragAssets _dragAssets;
private DragActorType _dragActorType;
private DragControlType _dragControlType;
private DragScriptItems _dragScriptItems;
private DragHandlers _dragHandlers;
@@ -83,7 +84,12 @@ namespace FlaxEditor.Windows.Assets
private static bool ValidateDragActorType(ScriptType actorType)
{
return true;
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
}
private static bool ValidateDragControlType(ScriptType controlType)
{
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
}
private static bool ValidateDragScriptItem(ScriptItem script)
@@ -113,6 +119,13 @@ namespace FlaxEditor.Windows.Assets
}
if (_dragActorType.OnDragEnter(data))
return _dragActorType.Effect;
if (_dragControlType == null)
{
_dragControlType = new DragControlType(ValidateDragControlType);
_dragHandlers.Add(_dragControlType);
}
if (_dragControlType.OnDragEnter(data))
return _dragControlType.Effect;
if (_dragScriptItems == null)
{
_dragScriptItems = new DragScriptItems(ValidateDragScriptItem);
@@ -176,6 +189,27 @@ namespace FlaxEditor.Windows.Assets
}
result = DragDropEffect.Move;
}
// Drag control type
else if (_dragControlType != null && _dragControlType.HasValidDrag)
{
for (int i = 0; i < _dragControlType.Objects.Count; i++)
{
var item = _dragControlType.Objects[i];
var control = item.CreateInstance() as Control;
if (control == null)
{
Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName);
continue;
}
var uiControl = new UIControl
{
Control = control,
Name = item.Name,
};
_window.Spawn(uiControl);
}
result = DragDropEffect.Move;
}
// Drag script item
else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag)
{
@@ -207,6 +241,7 @@ namespace FlaxEditor.Windows.Assets
_window = null;
_dragAssets = null;
_dragActorType = null;
_dragControlType = null;
_dragScriptItems = null;
_dragHandlers?.Clear();
_dragHandlers = null;
@@ -450,6 +485,7 @@ namespace FlaxEditor.Windows.Assets
// Create undo action
var action = new CustomDeleteActorsAction(new List<SceneGraphNode>(1) { actorNode }, true);
Undo.AddAction(action);
Focus();
Select(actorNode);
}
@@ -131,7 +131,7 @@ namespace FlaxEditor.Windows
public void ClearItemsSearch()
{
// Skip if already cleared
if (_itemsSearchBox.TextLength == 0 && !_viewDropdown.HasSelection)
if (_itemsSearchBox.TextLength == 0)
return;
IsLayoutLocked = true;
+2
View File
@@ -542,6 +542,8 @@ namespace FlaxEditor.Windows
return;
}
newShortName = newShortName.Trim();
// Cache data
string extension = item.IsFolder ? "" : Path.GetExtension(item.Path);
var newPath = StringUtils.CombinePaths(item.ParentFolder.Path, newShortName + extension);
+6 -2
View File
@@ -470,6 +470,10 @@ namespace FlaxEditor.Windows
IsMaximized = false;
IsBorderless = false;
Cursor = CursorType.Default;
Screen.CursorLock = CursorLockMode.None;
if (Screen.MainWindow.IsMouseTracking)
Screen.MainWindow.EndTrackingMouse();
RootControl.GameRoot.EndMouseCapture();
}
/// <inheritdoc />
@@ -478,7 +482,7 @@ namespace FlaxEditor.Windows
base.OnMouseLeave();
// Remove focus from game window when mouse moves out and the cursor is hidden during game
if ((IsFocused || ContainsFocus) && Parent != null && Editor.IsPlayMode && !Screen.CursorVisible)
if (ContainsFocus && Parent != null && Editor.IsPlayMode && !Screen.CursorVisible && Screen.CursorLock == CursorLockMode.None)
{
Parent.Focus();
}
@@ -856,7 +860,7 @@ namespace FlaxEditor.Windows
{
var alpha = Mathf.Saturate(-animTime / fadeOutTime);
var rect = new Rectangle(new Float2(6), Size - 12);
var text = "Press Shift+F11 to unlock the mouse";
var text = $"Press {Editor.Options.Options.Input.DebuggerUnlockMouse} to unlock the mouse";
Render2D.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far);
Render2D.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far);
}
+1 -1
View File
@@ -281,7 +281,7 @@ namespace FlaxEditor.Windows
if (IsLayoutLocked)
return;
_hScroll.Maximum = _output.TextSize.X;
_hScroll.Maximum = Mathf.Max(_output.TextSize.X, _hScroll.Minimum);
_vScroll.Maximum = Mathf.Max(_output.TextSize.Y - _output.Height, _vScroll.Minimum);
}
+37 -1
View File
@@ -30,6 +30,7 @@ namespace FlaxEditor.Windows
private DragAssets _dragAssets;
private DragActorType _dragActorType;
private DragControlType _dragControlType;
private DragScriptItems _dragScriptItems;
private DragHandlers _dragHandlers;
@@ -275,7 +276,12 @@ namespace FlaxEditor.Windows
private static bool ValidateDragActorType(ScriptType actorType)
{
return true;
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
}
private static bool ValidateDragControlType(ScriptType controlType)
{
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
}
private static bool ValidateDragScriptItem(ScriptItem script)
@@ -390,6 +396,13 @@ namespace FlaxEditor.Windows
}
if (_dragActorType.OnDragEnter(data) && result == DragDropEffect.None)
return _dragActorType.Effect;
if (_dragControlType == null)
{
_dragControlType = new DragControlType(ValidateDragControlType);
_dragHandlers.Add(_dragControlType);
}
if (_dragControlType.OnDragEnter(data) && result == DragDropEffect.None)
return _dragControlType.Effect;
if (_dragScriptItems == null)
{
_dragScriptItems = new DragScriptItems(ValidateDragScriptItem);
@@ -462,6 +475,28 @@ namespace FlaxEditor.Windows
}
result = DragDropEffect.Move;
}
// Drag control type
else if (_dragControlType != null && _dragControlType.HasValidDrag)
{
for (int i = 0; i < _dragControlType.Objects.Count; i++)
{
var item = _dragControlType.Objects[i];
var control = item.CreateInstance() as Control;
if (control == null)
{
Editor.LogWarning("Failed to spawn UIControl with control type " + item.TypeName);
continue;
}
var uiControl = new UIControl
{
Control = control,
Name = item.Name,
};
Level.SpawnActor(uiControl);
Editor.Scene.MarkSceneEdited(uiControl.Scene);
}
result = DragDropEffect.Move;
}
// Drag script item
else if (_dragScriptItems != null && _dragScriptItems.HasValidDrag)
{
@@ -495,6 +530,7 @@ namespace FlaxEditor.Windows
{
_dragAssets = null;
_dragActorType = null;
_dragControlType = null;
_dragScriptItems = null;
_dragHandlers?.Clear();
_dragHandlers = null;
+52
View File
@@ -191,6 +191,52 @@ namespace FlaxEditor.Windows
CreateGroupWithList(_actorGroups, "GUI");
CreateGroupWithList(_actorGroups, "Other");
// Add control types to tabs
foreach (var controlType in Editor.Instance.CodeEditing.Controls.Get())
{
if (controlType.IsAbstract)
continue;
_groupSearch.AddChild(CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType));
ActorToolboxAttribute attribute = null;
foreach (var e in controlType.GetAttributes(false))
{
if (e is ActorToolboxAttribute actorToolboxAttribute)
{
attribute = actorToolboxAttribute;
break;
}
}
if (attribute == null)
continue;
var groupName = attribute.Group.Trim();
// Check if tab already exists and add it to the tab
var actorTabExists = false;
foreach (var child in _actorGroups.Children)
{
if (child is Tab tab)
{
if (string.Equals(tab.Text, groupName, StringComparison.OrdinalIgnoreCase))
{
var tree = tab.GetChild<Panel>().GetChild<Tree>();
if (tree != null)
{
tree.AddChild(string.IsNullOrEmpty(attribute.Name) ? CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType) : CreateControlItem(attribute.Name, controlType));
tree.SortChildren();
}
actorTabExists = true;
break;
}
}
}
if (actorTabExists)
continue;
var group = CreateGroupWithList(_actorGroups, groupName);
group.AddChild(string.IsNullOrEmpty(attribute.Name) ? CreateControlItem(Utilities.Utils.GetPropertyNameUI(controlType.Name), controlType) : CreateControlItem(attribute.Name, controlType));
group.SortChildren();
}
// Add other actor types to respective tab based on attribute
foreach (var actorType in Editor.CodeEditing.Actors.Get())
{
@@ -304,6 +350,11 @@ namespace FlaxEditor.Windows
return new ScriptTypeItem(name, type, GUI.Drag.DragActorType.GetDragData(type));
}
private Item CreateControlItem(string name, ScriptType type)
{
return new ScriptTypeItem(name, type, GUI.Drag.DragControlType.GetDragData(type));
}
private ContainerControl CreateGroupWithList(Tabs parentTabs, string title, float topOffset = 0)
{
var tab = parentTabs.AddTab(new Tab(title));
@@ -316,6 +367,7 @@ namespace FlaxEditor.Windows
var tree = new Tree(false)
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Margin = new Margin(0, 0, 0, panel.ScrollBarsSize),
IsScrollable = true,
Parent = panel
};
@@ -163,7 +163,10 @@ namespace Serialization
{
const auto& keyframesArray = mKeyframes->value.GetArray();
auto& keyframes = v.GetKeyframes();
const int32 newCount = keyframesArray.Size() - keyframes.Count();
keyframes.Resize(keyframesArray.Size());
for (int32 i = 0; i < newCount; i++)
keyframes[keyframes.Count() + i - newCount] = KeyFrame(0.0f, AnimationUtils::GetZero<T>());
for (rapidjson::SizeType i = 0; i < keyframesArray.Size(); i++)
Deserialize(keyframesArray[i], keyframes[i], modifier);
}
@@ -102,8 +102,8 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float
Swap(eventTimeMin, eventTimeMax);
}
}
const float eventTime = animPos / static_cast<float>(anim->Data.FramesPerSecond);
const float eventDeltaTime = (animPos - animPrevPos) / static_cast<float>(anim->Data.FramesPerSecond);
const float eventTime = (float)(animPos / anim->Data.FramesPerSecond);
const float eventDeltaTime = (float)((animPos - animPrevPos) / anim->Data.FramesPerSecond);
for (const auto& track : anim->Events)
{
for (const auto& k : track.Second.GetKeyframes())
@@ -211,7 +211,7 @@ float GetAnimSamplePos(float length, Animation* anim, float pos, float speed)
}
if (animPos < 0)
animPos = animLength + animPos;
animPos *= static_cast<float>(anim->Data.FramesPerSecond);
animPos = (float)(animPos * anim->Data.FramesPerSecond);
return animPos;
}
@@ -265,7 +265,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
float nestedAnimPrevPos = animPrevPos - nestedAnim.Time;
const float nestedAnimLength = nestedAnim.Anim->GetLength();
const float nestedAnimSpeed = nestedAnim.Speed * speed;
const float frameRateMatchScale = nestedAnimSpeed / (float)anim->Data.FramesPerSecond;
const float frameRateMatchScale = (float)(nestedAnimSpeed / anim->Data.FramesPerSecond);
nestedAnimPos = nestedAnimPos * frameRateMatchScale;
nestedAnimPrevPos = nestedAnimPrevPos * frameRateMatchScale;
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
@@ -363,8 +363,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
// Check if animation looped
if (animPos < animPrevPos)
{
const float endPos = anim->GetLength() * static_cast<float>(anim->Data.FramesPerSecond);
const float timeToEnd = endPos - animPrevPos;
const float endPos = (float)(anim->GetLength() * anim->Data.FramesPerSecond);
Transform rootBegin = refPose;
rootChannel.Evaluate(0, &rootBegin, false);
@@ -372,16 +371,13 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
Transform rootEnd = refPose;
rootChannel.Evaluate(endPos, &rootEnd, false);
//rootChannel.Evaluate(animPos - timeToEnd, &rootNow, true);
// Complex motion calculation to preserve the looped movement
// (end - before + now - begin)
// It sums the motion since the last update to anim end and since the start to now
if (motionPosition)
srcNode.Translation = (rootEnd.Translation - rootBefore.Translation + rootNode.Translation - rootBegin.Translation) * motionPositionMask;
if (motionRotation)
srcNode.Orientation = rootEnd.Orientation * rootBefore.Orientation.Conjugated() * (rootNode.Orientation * rootBegin.Orientation.Conjugated());
//srcNode.Orientation = Quaternion::Identity;
srcNode.Orientation = (rootBefore.Orientation.Conjugated() * rootEnd.Orientation) * (rootBegin.Orientation.Conjugated() * rootNode.Orientation);
}
else
{
@@ -1165,21 +1161,21 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
const auto nodes = node->GetNodes(this);
const auto basePoseNodes = static_cast<AnimGraphImpulse*>(valueA.AsPointer);
const auto blendPoseNodes = static_cast<AnimGraphImpulse*>(valueB.AsPointer);
const auto& refrenceNodes = _graph.BaseModel.Get()->GetNodes();
Transform t, basePoseTransform, blendPoseTransform, refrenceTransform;
const auto& refNodes = _graph.BaseModel.Get()->GetNodes();
Transform t, basePoseTransform, blendPoseTransform, refTransform;
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
{
basePoseTransform = basePoseNodes->Nodes[i];
blendPoseTransform = blendPoseNodes->Nodes[i];
refrenceTransform = refrenceNodes[i].LocalTransform;
refTransform = refNodes[i].LocalTransform;
// base + (blend - refrence) = transform
t.Translation = basePoseTransform.Translation + (blendPoseTransform.Translation - refrenceTransform.Translation);
auto diff = Quaternion::Invert(refrenceTransform.Orientation) * blendPoseTransform.Orientation;
// base + (blend - reference)
t.Translation = basePoseTransform.Translation + (blendPoseTransform.Translation - refTransform.Translation);
auto diff = Quaternion::Invert(refTransform.Orientation) * blendPoseTransform.Orientation;
t.Orientation = basePoseTransform.Orientation * diff;
t.Scale = basePoseTransform.Scale + (blendPoseTransform.Scale - refrenceTransform.Scale);
t.Scale = basePoseTransform.Scale + (blendPoseTransform.Scale - refTransform.Scale);
//lerp base and transform
// Lerp base and transform
Transform::Lerp(basePoseTransform, t, alpha, nodes->Nodes[i]);
}
Transform::Lerp(basePoseNodes->RootMotion, basePoseNodes->RootMotion + blendPoseNodes->RootMotion, alpha, nodes->RootMotion);
+21 -19
View File
@@ -80,7 +80,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
// Calculate new positions for joint and end effector
Vector3 newEndEffectorPos = targetPosition;
Vector3 newMidJointPos = midJointPos;
if (toTargetLength >= totalLimbLength)
{
// Target is beyond the reach of the limb
@@ -90,13 +89,9 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
Vector3 rootToPole = (poleVector - rootTransform.Translation).GetNormalized();
Vector3 slightBendDirection = Vector3::Cross(rootToEnd, rootToPole);
if (slightBendDirection.LengthSquared() < ZeroTolerance * ZeroTolerance)
{
slightBendDirection = Vector3::Up;
}
else
{
slightBendDirection.Normalize();
}
// Calculate the direction from root to mid joint with a slight offset towards the pole vector
Vector3 midJointDirection = Vector3::Cross(slightBendDirection, rootToEnd).GetNormalized();
@@ -117,9 +112,16 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
projJointDist *= -1.0f;
newMidJointPos = rootTransform.Translation + projJointDist * toTargetDir + jointLineDist * bendDirection;
}
// TODO: fix the new IK impl (https://github.com/FlaxEngine/FlaxEngine/pull/2421) to properly work for character from https://github.com/PrecisionRender/CharacterControllerPro
#define OLD 0
// Update root joint orientation
{
#if OLD
const Vector3 oldDir = (midJointPos - rootTransform.Translation).GetNormalized();
const Vector3 newDir = (newMidJointPos - rootTransform.Translation).GetNormalized();
const Quaternion deltaRotation = Quaternion::FindBetween(oldDir, newDir);
rootTransform.Orientation = deltaRotation * rootTransform.Orientation;
#else
// Vector from root joint to mid joint (local Y-axis direction)
Vector3 localY = (newMidJointPos - rootTransform.Translation).GetNormalized();
@@ -136,20 +138,23 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
localZ = Vector3::Cross(localX, localY).GetNormalized();
// Construct a rotation from the orthogonal basis vectors
Quaternion newRootJointOrientation = Quaternion::LookRotation(localZ, localY);
// Apply the new rotation to the root joint
rootTransform.Orientation = newRootJointOrientation;
rootTransform.Orientation = Quaternion::LookRotation(localZ, localY);
#endif
}
// Update mid joint orientation to point Y-axis towards the end effector and Z-axis perpendicular to the IK plane
{
#if OLD
const Vector3 oldDir = (endEffectorTransform.Translation - midJointPos).GetNormalized();
const Vector3 newDir = (newEndEffectorPos - newMidJointPos).GetNormalized();
const Quaternion deltaRotation = Quaternion::FindBetween(oldDir, newDir);
midJointTransform.Orientation = deltaRotation * midJointTransform.Orientation;
#else
// Vector from mid joint to end effector (local Y-axis direction after rotation)
Vector3 midToEnd = (newEndEffectorPos - newMidJointPos).GetNormalized();
// Calculate the plane normal using the root, mid joint, and end effector positions (will be the local Z-axis direction)
Vector3 rootToMid = (newMidJointPos - rootTransform.Translation).GetNormalized();
Vector3 planeNormal = Vector3::Cross(rootToMid, midToEnd).GetNormalized();
// Vector from mid joint to end effector (local Y-axis direction)
Vector3 localY = (newEndEffectorPos - newMidJointPos).GetNormalized();
@@ -157,21 +162,18 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
// Calculate the plane normal using the root, mid joint, and end effector positions (local Z-axis direction)
Vector3 localZ = Vector3::Cross(rootToMid, localY).GetNormalized();
//// Calculate the local X-axis direction, should be perpendicular to the Y and Z axes
// Calculate the local X-axis direction, should be perpendicular to the Y and Z axes
Vector3 localX = Vector3::Cross(localY, localZ).GetNormalized();
// Correct the local Z-axis direction based on the cross product of X and Y to ensure orthogonality
localZ = Vector3::Cross(localX, localY).GetNormalized();
// Construct a rotation from the orthogonal basis vectors
// The axes are used differently here than a standard LookRotation to align Z towards the end and Y perpendicular
Quaternion newMidJointOrientation = Quaternion::LookRotation(localZ, localY); // Assuming FromLookRotation creates a rotation with the first vector as forward and the second as up
// Apply the new rotation to the mid joint
midJointTransform.Orientation = newMidJointOrientation;
midJointTransform.Translation = newMidJointPos;
midJointTransform.Orientation = Quaternion::LookRotation(localZ, localY);
#endif
}
// Update end effector transform
// Update mid and end locations
midJointTransform.Translation = newMidJointPos;
endEffectorTransform.Translation = newEndEffectorPos;
}
@@ -187,6 +187,9 @@ Asset::LoadResult Material::load()
#endif
)
{
// Guard file with the lock during shader generation (prevents FlaxStorage::Tick from messing with the file)
auto lock = Storage->Lock();
// Prepare
MaterialGenerator generator;
generator.Error.Bind(&OnGeneratorError);
@@ -886,7 +886,8 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
instance = eatBox(node, box->FirstConnection());
else
instance.SetObject(object);
if (!instance.AsObject)
ScriptingObject* instanceObj = (ScriptingObject*)instance;
if (!instanceObj)
{
LOG(Error, "Cannot bind event to null object.");
PrintStack(LogType::Error);
@@ -928,13 +929,13 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
}
eventBinding->BindedMethods.Add(method);
if (eventBinding->BindedMethods.Count() == 1)
(*eventBinder)(instance.AsObject, object, true);
(*eventBinder)(instanceObj, object, true);
}
else if (eventBinding)
{
// Unbind from the event
if (eventBinding->BindedMethods.Count() == 1)
(*eventBinder)(instance.AsObject, object, false);
(*eventBinder)(instanceObj, object, false);
eventBinding->BindedMethods.Remove(method);
}
}
@@ -252,6 +252,7 @@ void ContentStorageSystem::Job(int32 index)
{
PROFILE_CPU_NAMED("ContentStorage.Job");
const double time = Platform::GetTimeSeconds();
ScopeLock lock(Locker);
for (auto i = StorageMap.Begin(); i.IsNotEnd(); ++i)
{
@@ -271,7 +272,7 @@ void ContentStorageSystem::Job(int32 index)
}
else
{
storage->Tick();
storage->Tick(time);
}
}
}
+13 -10
View File
@@ -218,10 +218,7 @@ const Char* TypeId2TypeName(const uint32 typeId)
}
FlaxStorage::FlaxStorage(const StringView& path)
: _refCount(0)
, _chunksLock(0)
, _version(0)
, _path(path)
: _path(path)
{
}
@@ -242,6 +239,7 @@ FlaxStorage::~FlaxStorage()
if (stream)
Delete(stream);
}
Platform::AtomicStore(&_files, 0);
#endif
}
@@ -1327,6 +1325,7 @@ FileReadStream* FlaxStorage::OpenFile()
LOG(Error, "Cannot open Flax Storage file \'{0}\'.", _path);
return nullptr;
}
Platform::InterlockedIncrement(&_files);
// Create file reading stream
stream = New<FileReadStream>(file);
@@ -1336,6 +1335,10 @@ FileReadStream* FlaxStorage::OpenFile()
bool FlaxStorage::CloseFileHandles()
{
if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0)
{
return false;
}
PROFILE_CPU();
// Note: this is usually called by the content manager when this file is not used or on exit
@@ -1367,7 +1370,7 @@ bool FlaxStorage::CloseFileHandles()
return true; // Failed, someone is still accessing the file
// Close file handles (from all threads)
Array<FileReadStream*> streams;
Array<FileReadStream*, InlinedAllocation<8>> streams;
_file.GetValues(streams);
for (FileReadStream* stream : streams)
{
@@ -1375,6 +1378,7 @@ bool FlaxStorage::CloseFileHandles()
Delete(stream);
}
_file.Clear();
Platform::AtomicStore(&_files, 0);
return false;
}
@@ -1394,19 +1398,18 @@ void FlaxStorage::Dispose()
_version = 0;
}
void FlaxStorage::Tick()
void FlaxStorage::Tick(double time)
{
// Check if chunks are locked
// Skip if file is in use
if (Platform::AtomicRead(&_chunksLock) != 0)
return;
const double now = Platform::GetTimeSeconds();
bool wasAnyUsed = false;
const float unusedDataChunksLifetime = ContentStorageManager::UnusedDataChunksLifetime.GetTotalSeconds();
for (int32 i = 0; i < _chunks.Count(); i++)
{
auto chunk = _chunks.Get()[i];
const bool wasUsed = (now - chunk->LastAccessTime) < unusedDataChunksLifetime;
const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime;
if (!wasUsed && chunk->IsLoaded())
{
chunk->Unload();
@@ -1414,7 +1417,7 @@ void FlaxStorage::Tick()
wasAnyUsed |= wasUsed;
}
// Release file handles in none of chunks was not used
// Release file handles in none of chunks is in use
if (!wasAnyUsed && Platform::AtomicRead(&_chunksLock) == 0)
{
CloseFileHandles();
+5 -4
View File
@@ -87,8 +87,9 @@ public:
protected:
// State
int64 _refCount;
int64 _chunksLock;
int64 _refCount = 0;
int64 _chunksLock = 0;
int64 _files = 0;
double _lastRefLostTime;
CriticalSection _loadLocker;
@@ -97,7 +98,7 @@ protected:
Array<FlaxChunk*> _chunks;
// Metadata
uint32 _version;
uint32 _version = 0;
String _path;
protected:
@@ -415,7 +416,7 @@ public:
/// <summary>
/// Ticks this instance.
/// </summary>
void Tick();
void Tick(double time);
#if USE_EDITOR
void OnRename(const StringView& newPath);
+1 -1
View File
@@ -42,7 +42,7 @@ public:
int32 ContentKey = 0;
/// <summary>
/// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett).
/// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett). Enabled by default for `Release` builds.
/// </summary>
API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"General\")")
bool ForDistribution = false;
@@ -18,7 +18,7 @@ namespace FlaxEditor.Content.Settings
/// <summary>
/// The layers names.
/// </summary>
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(ReadOnly = true, Display = CollectionAttribute.DisplayType.Inline)]
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = true, Display = CollectionAttribute.DisplayType.Inline)]
public string[] Layers = new string[32];
/// <summary>
+21 -19
View File
@@ -32,41 +32,39 @@ Color::Color(const Color32& color)
{
}
Color Color::FromHex(const String& hexString, bool& isValid)
Color Color::FromHex(const String& hex, bool& isValid)
{
int32 r, g, b, a = 255;
isValid = true;
int32 startIndex = !hexString.IsEmpty() && hexString[0] == Char('#') ? 1 : 0;
if (hexString.Length() == 3 + startIndex)
int32 startIndex = !hex.IsEmpty() && hex[0] == Char('#') ? 1 : 0;
if (hex.Length() == 3 + startIndex)
{
r = StringUtils::HexDigit(hexString[startIndex++]);
g = StringUtils::HexDigit(hexString[startIndex++]);
b = StringUtils::HexDigit(hexString[startIndex]);
r = StringUtils::HexDigit(hex[startIndex++]);
g = StringUtils::HexDigit(hex[startIndex++]);
b = StringUtils::HexDigit(hex[startIndex]);
r = (r << 4) + r;
g = (g << 4) + g;
b = (b << 4) + b;
}
else if (hexString.Length() == 6 + startIndex)
else if (hex.Length() == 6 + startIndex)
{
r = (StringUtils::HexDigit(hexString[startIndex + 0]) << 4) + StringUtils::HexDigit(hexString[startIndex + 1]);
g = (StringUtils::HexDigit(hexString[startIndex + 2]) << 4) + StringUtils::HexDigit(hexString[startIndex + 3]);
b = (StringUtils::HexDigit(hexString[startIndex + 4]) << 4) + StringUtils::HexDigit(hexString[startIndex + 5]);
r = (StringUtils::HexDigit(hex[startIndex + 0]) << 4) + StringUtils::HexDigit(hex[startIndex + 1]);
g = (StringUtils::HexDigit(hex[startIndex + 2]) << 4) + StringUtils::HexDigit(hex[startIndex + 3]);
b = (StringUtils::HexDigit(hex[startIndex + 4]) << 4) + StringUtils::HexDigit(hex[startIndex + 5]);
}
else if (hexString.Length() == 8 + startIndex)
else if (hex.Length() == 8 + startIndex)
{
r = (StringUtils::HexDigit(hexString[startIndex + 0]) << 4) + StringUtils::HexDigit(hexString[startIndex + 1]);
g = (StringUtils::HexDigit(hexString[startIndex + 2]) << 4) + StringUtils::HexDigit(hexString[startIndex + 3]);
b = (StringUtils::HexDigit(hexString[startIndex + 4]) << 4) + StringUtils::HexDigit(hexString[startIndex + 5]);
a = (StringUtils::HexDigit(hexString[startIndex + 6]) << 4) + StringUtils::HexDigit(hexString[startIndex + 7]);
r = (StringUtils::HexDigit(hex[startIndex + 0]) << 4) + StringUtils::HexDigit(hex[startIndex + 1]);
g = (StringUtils::HexDigit(hex[startIndex + 2]) << 4) + StringUtils::HexDigit(hex[startIndex + 3]);
b = (StringUtils::HexDigit(hex[startIndex + 4]) << 4) + StringUtils::HexDigit(hex[startIndex + 5]);
a = (StringUtils::HexDigit(hex[startIndex + 6]) << 4) + StringUtils::HexDigit(hex[startIndex + 7]);
}
else
{
r = g = b = 0;
isValid = false;
}
return FromBytes(r, g, b, a);
}
@@ -122,8 +120,9 @@ String Color::ToHexString() const
const byte r = static_cast<byte>(R * MAX_uint8);
const byte g = static_cast<byte>(G * MAX_uint8);
const byte b = static_cast<byte>(B * MAX_uint8);
const byte a = static_cast<byte>(A * MAX_uint8);
Char result[6];
Char result[8];
result[0] = digits[r >> 4 & 0x0f];
result[1] = digits[r & 0x0f];
@@ -134,7 +133,10 @@ String Color::ToHexString() const
result[4] = digits[b >> 4 & 0x0f];
result[5] = digits[b & 0x0f];
return String(result, 6);
result[6] = digits[a >> 4 & 0x0f];
result[7] = digits[a & 0x0f];
return String(result, 8);
}
bool Color::IsTransparent() const
+74 -17
View File
@@ -230,36 +230,93 @@ namespace FlaxEngine
}
/// <summary>
/// Creates <see cref="Color"/> from the RGB value and separate alpha channel.
/// Creates <see cref="Color"/> from the RGB value (bottom bits contain Blue) and separate alpha channel.
/// </summary>
/// <param name="rgb">The packed RGB value.</param>
/// <param name="rgb">The packed RGB value (bottom bits contain Blue).</param>
/// <param name="a">The alpha channel value.</param>
/// <returns>The color.</returns>
public static Color FromRGB(uint rgb, float a = 1.0f)
{
return new Color(
((rgb >> 16) & 0xff) / 255.0f,
((rgb >> 8) & 0xff) / 255.0f,
(rgb & 0xff) / 255.0f,
a);
return new Color(((rgb >> 16) & 0xff) / 255.0f, ((rgb >> 8) & 0xff) / 255.0f, (rgb & 0xff) / 255.0f, a);
}
/// <summary>
/// Creates <see cref="Color"/> from the RGBA value.
/// Creates <see cref="Color"/> from the ARGB value (bottom bits contain Blue).
/// </summary>
/// <param name="rgb">The packed RGBA value.</param>
/// <param name="argb">The packed ARGB value (bottom bits contain Blue).</param>
/// <returns>The color.</returns>
public static Color FromRGBA(uint rgb)
public static Color FromARGB(uint argb)
{
return new Color(
((rgb >> 16) & 0xff) / 255.0f,
((rgb >> 8) & 0xff) / 255.0f,
(rgb & 0xff) / 255.0f,
((rgb >> 24) & 0xff) / 255.0f);
return new Color(((argb >> 16) & 0xff) / 255.0f, ((argb >> 8) & 0xff) / 255.0f, ((argb >> 0) & 0xff) / 255.0f, ((argb >> 24) & 0xff) / 255.0f);
}
/// <summary>
/// Gets the color value as the hexadecimal string.
/// Creates <see cref="Color"/> from the RGBA value (bottom bits contain Alpha).
/// </summary>
/// <param name="rgba">The packed RGBA value (bottom bits Alpha Red).</param>
/// <returns>The color.</returns>
public static Color FromRGBA(uint rgba)
{
return new Color(((rgba >> 24) & 0xff) / 255.0f, ((rgba >> 16) & 0xff) / 255.0f, ((rgba >> 8) & 0xff) / 255.0f, ((rgba >> 0) & 0xff) / 255.0f);
}
/// <summary>
/// Creates <see cref="Color"/> from the Hex string.
/// </summary>
/// <param name="hex">The hexadecimal color string.</param>
/// <returns>The output color value.</returns>
public static Color FromHex(string hex)
{
FromHex(hex, out var color);
return color;
}
/// <summary>
/// Creates <see cref="Color"/> from the Hex string.
/// </summary>
/// <param name="hex">The hexadecimal color string.</param>
/// <param name="color">The output color value. Valid if method returns true.</param>
/// <returns>True if method was able to convert color, otherwise false.</returns>
public static bool FromHex(string hex, out Color color)
{
int r, g, b, a = 255;
bool isValid = true;
int startIndex = hex.Length != 0 && hex[0] == '#' ? 1 : 0;
if (hex.Length == 3 + startIndex)
{
r = StringUtils.HexDigit(hex[startIndex++]);
g = StringUtils.HexDigit(hex[startIndex++]);
b = StringUtils.HexDigit(hex[startIndex]);
r = (r << 4) + r;
g = (g << 4) + g;
b = (b << 4) + b;
}
else if (hex.Length == 6 + startIndex)
{
r = (StringUtils.HexDigit(hex[startIndex + 0]) << 4) + StringUtils.HexDigit(hex[startIndex + 1]);
g = (StringUtils.HexDigit(hex[startIndex + 2]) << 4) + StringUtils.HexDigit(hex[startIndex + 3]);
b = (StringUtils.HexDigit(hex[startIndex + 4]) << 4) + StringUtils.HexDigit(hex[startIndex + 5]);
}
else if (hex.Length == 8 + startIndex)
{
r = (StringUtils.HexDigit(hex[startIndex + 0]) << 4) + StringUtils.HexDigit(hex[startIndex + 1]);
g = (StringUtils.HexDigit(hex[startIndex + 2]) << 4) + StringUtils.HexDigit(hex[startIndex + 3]);
b = (StringUtils.HexDigit(hex[startIndex + 4]) << 4) + StringUtils.HexDigit(hex[startIndex + 5]);
a = (StringUtils.HexDigit(hex[startIndex + 6]) << 4) + StringUtils.HexDigit(hex[startIndex + 7]);
}
else
{
r = g = b = 0;
isValid = false;
}
color = new Color(r, g, b, a);
return isValid;
}
/// <summary>
/// Gets the color value as the hexadecimal string (in RGBA order).
/// </summary>
/// <returns>Hex string.</returns>
public string ToHexString()
@@ -287,7 +344,7 @@ namespace FlaxEngine
return new string(result);
}
/// <summary>
/// Creates <see cref="Color"/> from the text string (hex or color name).
/// </summary>
+18 -8
View File
@@ -126,9 +126,9 @@ public:
}
/// <summary>
/// Initializes from packed RGB value of the color and separate alpha channel value.
/// Initializes from packed RGB value (bottom bits contain Blue) of the color and separate alpha channel value.
/// </summary>
/// <param name="rgb">The packed RGB value.</param>
/// <param name="rgb">The packed RGB value (bottom bits contain Blue).</param>
/// <param name="a">The alpha channel.</param>
/// <returns>The color.</returns>
static Color FromRGB(uint32 rgb, float a = 1.0f)
@@ -137,22 +137,32 @@ public:
}
/// <summary>
/// Initializes from packed RGBA value.
/// Initializes from packed ARGB value (bottom bits contain Blue).
/// </summary>
/// <param name="rgba">The packed RGBA value.</param>
/// <param name="argb">The packed ARGB value (bottom bits contain Blue).</param>
/// <returns>The color.</returns>
static Color FromARGB(uint32 argb)
{
return Color((float)((argb >> 16) & 0xff) / 255.0f,(float)((argb >> 8) & 0xff) / 255.0f, (float)((argb >> 0) & 0xff) / 255.0f, (float)((argb >> 24) & 0xff) / 255.0f);
}
/// <summary>
/// Initializes from packed RGBA value (bottom bits contain Alpha).
/// </summary>
/// <param name="rgba">The packed RGBA value (bottom bits contain Alpha).</param>
/// <returns>The color.</returns>
static Color FromRGBA(uint32 rgba)
{
return Color(static_cast<float>(rgba >> 16 & 0xff) / 255.0f, static_cast<float>(rgba >> 8 & 0xff) / 255.0f, static_cast<float>(rgba & 0xff) / 255.0f, static_cast<float>(rgba >> 24 & 0xff) / 255.0f);
return Color((float)((rgba >> 24) & 0xff) / 255.0f,(float)((rgba >> 16) & 0xff) / 255.0f, (float)((rgba >> 8) & 0xff) / 255.0f, (float)((rgba >> 0) & 0xff) / 255.0f);
}
static Color FromHex(const String& hexString)
static Color FromHex(const String& hex)
{
bool isValid;
return FromHex(hexString, isValid);
return FromHex(hex, isValid);
}
static Color FromHex(const String& hexString, bool& isValid);
static Color FromHex(const String& hex, bool& isValid);
/// <summary>
/// Creates RGB color from Hue[0-360], Saturation[0-1] and Value[0-1].
@@ -586,8 +586,17 @@ namespace FlaxEngine.Interop
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
{
Type elementType = Unsafe.As<TypeHolder>(elementTypeHandle.Target);
Type classType = ArrayFactory.GetArrayType(elementType);
return GetTypeManagedHandle(classType);
Type arrayType = ArrayFactory.GetArrayType(elementType);
return GetTypeManagedHandle(arrayType);
}
[UnmanagedCallersOnly]
internal static ManagedHandle GetArrayTypeFromWrappedArray(ManagedHandle arrayHandle)
{
ManagedArray managedArray = Unsafe.As<ManagedArray>(arrayHandle.Target);
Type elementType = managedArray.ArrayType.GetElementType();
Type arrayType = ArrayFactory.GetArrayType(elementType);
return GetTypeManagedHandle(arrayType);
}
[UnmanagedCallersOnly]
+3 -3
View File
@@ -404,7 +404,7 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
drawCall.Material = material;
drawCall.World = world;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = _sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue();
drawCall.ObjectRadius = (float)_sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue();
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = world;
drawCall.Surface.Lightmap = nullptr;
@@ -472,7 +472,7 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
drawCall.Material = material;
drawCall.World = *info.World;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
drawCall.ObjectRadius = (float)info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
@@ -534,7 +534,7 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
drawCall.Material = material;
drawCall.World = *info.World;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
drawCall.ObjectRadius = (float)info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
@@ -246,7 +246,7 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
drawCall.Material = material;
drawCall.World = *info.World;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
drawCall.ObjectRadius = (float)info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Lightmap = nullptr;
@@ -289,7 +289,7 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
drawCall.Material = material;
drawCall.World = *info.World;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
drawCall.ObjectRadius = (float)info.Bounds.Radius; // TODO: should it be kept in sync with ObjectPosition?
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Lightmap = nullptr;
+18 -18
View File
@@ -4,9 +4,9 @@
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/ISerializable.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Content/SoftAssetReference.h"
#include "Engine/Core/ISerializable.h"
#include "Engine/Content/Assets/Texture.h"
#include "Engine/Content/Assets/MaterialBase.h"
@@ -526,13 +526,13 @@ API_STRUCT() struct FLAXENGINE_API ToneMappingSettings : ISerializable
ToneMappingSettingsOverride OverrideFlags = Override::None;
/// <summary>
/// Adjusts the white balance in relation to the temperature of the light in the scene. When the light temperature and this one match the light will appear white. When a value is used that is higher than the light in the scene it will yield a "warm" or yellow color, and, conversely, if the value is lower, it would yield a "cool" or blue color. The default value is `6500`.
/// Adjusts the white balance in relation to the temperature of the light in the scene. When the light temperature and this one match the light will appear white. When a value is used that is higher than the light in the scene it will yield a "warm" or yellow color, and, conversely, if the value is lower, it would yield a "cool" or blue color.
/// </summary>
API_FIELD(Attributes="Limit(1500, 15000), EditorOrder(0), PostProcessSetting((int)ToneMappingSettingsOverride.WhiteTemperature)")
float WhiteTemperature = 6500.0f;
/// <summary>
/// Adjusts the white balance temperature tint for the scene by adjusting the cyan and magenta color ranges. Ideally, this setting should be used once you've adjusted the white balance temperature to get accurate colors. Under some light temperatures, the colors may appear to be more yellow or blue. This can be used to balance the resulting color to look more natural. The default value is `0`.
/// Adjusts the white balance temperature tint for the scene by adjusting the cyan and magenta color ranges. Ideally, this setting should be used once you've adjusted the white balance temperature to get accurate colors. Under some light temperatures, the colors may appear to be more yellow or blue. This can be used to balance the resulting color to look more natural.
/// </summary>
API_FIELD(Attributes="Limit(-1, 1, 0.001f), EditorOrder(1), PostProcessSetting((int)ToneMappingSettingsOverride.WhiteTint)")
float WhiteTint = 0.0f;
@@ -1079,7 +1079,7 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
CameraArtifactsSettingsOverride OverrideFlags = Override::None;
/// <summary>
/// Strength of the vignette effect. Value 0 hides it. The default value is 0.4.
/// Strength of the vignette effect. Value 0 hides it.
/// </summary>
API_FIELD(Attributes="Limit(0, 2, 0.001f), EditorOrder(0), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteIntensity)")
float VignetteIntensity = 0.4f;
@@ -1091,19 +1091,19 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
Float3 VignetteColor = Float3(0, 0, 0.001f);
/// <summary>
/// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125.
/// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape.
/// </summary>
API_FIELD(Attributes="Limit(0.0001f, 2.0f, 0.001f), EditorOrder(2), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteShapeFactor)")
float VignetteShapeFactor = 0.125f;
/// <summary>
/// Intensity of the grain filter. A value of 0 hides it. The default value is 0.005.
/// Intensity of the grain filter. A value of 0 hides it.
/// </summary>
API_FIELD(Attributes="Limit(0.0f, 2.0f, 0.005f), EditorOrder(3), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainAmount)")
float GrainAmount = 0.006f;
/// <summary>
/// Size of the grain particles. The default value is 1.6.
/// Size of the grain particles.
/// </summary>
API_FIELD(Attributes="Limit(1.0f, 3.0f, 0.01f), EditorOrder(4), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainParticleSize)")
float GrainParticleSize = 1.6f;
@@ -1731,49 +1731,49 @@ API_STRUCT() struct FLAXENGINE_API ScreenSpaceReflectionsSettings : ISerializabl
ReflectionsTraceMode TraceMode = ReflectionsTraceMode::ScreenTracing;
/// <summary>
/// The depth buffer downscale option to optimize raycast performance. Full gives better quality, but half improves performance. The default value is half.
/// The depth buffer downscale option to optimize raycast performance. Full gives better quality, but half improves performance.
/// </summary>
API_FIELD(Attributes="EditorOrder(2), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.DepthResolution)")
ResolutionMode DepthResolution = ResolutionMode::Half;
/// <summary>
/// The raycast resolution. Full gives better quality, but half improves performance. The default value is half.
/// The raycast resolution. Full gives better quality, but half improves performance.
/// </summary>
API_FIELD(Attributes="EditorOrder(3), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.RayTracePassResolution)")
ResolutionMode RayTracePassResolution = ResolutionMode::Half;
/// <summary>
/// The reflection spread parameter. This value controls source roughness effect on reflections blur. Smaller values produce wider reflections spread but also introduce more noise. Higher values provide more mirror-like reflections. Default value is 0.82.
/// The reflection spread parameter. This value controls source roughness effect on reflections blur. Smaller values produce wider reflections spread but also introduce more noise. Higher values provide more mirror-like reflections.
/// </summary>
API_FIELD(Attributes="Limit(0, 1.0f, 0.01f), EditorOrder(10), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.BRDFBias), EditorDisplay(null, \"BRDF Bias\")")
float BRDFBias = 0.82f;
/// <summary>
/// The maximum amount of roughness a material must have to reflect the scene. For example, if this value is set to 0.4, only materials with a roughness value of 0.4 or below reflect the scene. The default value is 0.45.
/// The maximum amount of roughness a material must have to reflect the scene. For example, if this value is set to 0.4, only materials with a roughness value of 0.4 or below reflect the scene.
/// </summary>
API_FIELD(Attributes="Limit(0, 1.0f, 0.01f), EditorOrder(15), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.RoughnessThreshold)")
float RoughnessThreshold = 0.45f;
/// <summary>
/// The offset of the raycast origin. Lower values produce more correct reflection placement, but produce more artifacts. We recommend values of 0.3 or lower. The default value is 0.1.
/// The offset of the raycast origin. Lower values produce more correct reflection placement, but produce more artifacts. We recommend values of 0.3 or lower.
/// </summary>
API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(20), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.WorldAntiSelfOcclusionBias)")
float WorldAntiSelfOcclusionBias = 0.1f;
/// <summary>
/// The raycast resolution. Full gives better quality, but half improves performance. The default value is half.
/// The raycast resolution. Full gives better quality, but half improves performance.
/// </summary>
API_FIELD(Attributes="EditorOrder(25), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.ResolvePassResolution)")
ResolutionMode ResolvePassResolution = ResolutionMode::Full;
/// <summary>
/// The number of rays used to resolve the reflection color. Higher values provide better quality but reduce effect performance. Default value is 4. Use 1 for the highest speed.
/// The number of rays used to resolve the reflection color. Higher values provide better quality but reduce effect performance. Use value of 1 for the best performance at cost of quality.
/// </summary>
API_FIELD(Attributes="Limit(1, 8), EditorOrder(26), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.ResolveSamples)")
int32 ResolveSamples = 4;
/// <summary>
/// The point at which the far edges of the reflection begin to fade. Has no effect on performance. The default value is 0.1.
/// The point at which the far edges of the reflection begin to fade. Has no effect on performance.
/// </summary>
API_FIELD(Attributes="Limit(0, 1.0f, 0.02f), EditorOrder(30), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.EdgeFadeFactor)")
float EdgeFadeFactor = 0.1f;
@@ -1803,13 +1803,13 @@ API_STRUCT() struct FLAXENGINE_API ScreenSpaceReflectionsSettings : ISerializabl
bool TemporalEffect = true;
/// <summary>
/// The intensity of the temporal effect. Lower values produce reflections faster, but more noise. The default value is 8.
/// The intensity of the temporal effect. Lower values produce reflections faster, but more noise.
/// </summary>
API_FIELD(Attributes="Limit(0, 20.0f, 0.5f), EditorOrder(55), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalScale)")
float TemporalScale = 8.0f;
/// <summary>
/// Defines how quickly reflections blend between the reflection in the current frame and the history buffer. Lower values produce reflections faster, but with more jittering. If the camera in your game doesn't move much, we recommend values closer to 1. The default value is 0.8.
/// Defines how quickly reflections blend between the reflection in the current frame and the history buffer. Lower values produce reflections faster, but with more jittering. If the camera in your game doesn't move much, we recommend values closer to 1.
/// </summary>
API_FIELD(Attributes="Limit(0.05f, 1.0f, 0.01f), EditorOrder(60), PostProcessSetting((int)ScreenSpaceReflectionsSettingsOverride.TemporalResponse)")
float TemporalResponse = 0.8f;
@@ -2015,7 +2015,7 @@ API_STRUCT() struct FLAXENGINE_API PostProcessSettings : ISerializable
ScreenSpaceReflectionsSettings ScreenSpaceReflections;
/// <summary>
/// The anti-aliasing effect settings.
/// The antialiasing effect settings.
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Anti Aliasing\"), EditorOrder(1100), JsonProperty(\"AA\")")
AntiAliasingSettings AntiAliasing;
+1 -1
View File
@@ -42,7 +42,7 @@ void RenderTask::DrawAll()
// Sort tasks (by Order property)
Sorting::QuickSortObj(Tasks.Get(), Tasks.Count());
// Render all that shit
// Render all tasks
for (auto task : Tasks)
{
if (task->CanDraw())
+3 -3
View File
@@ -349,9 +349,9 @@ public:
// Applies the render origin to the transformation instance matrix.
FORCE_INLINE void GetWorldMatrix(Matrix& world) const
{
world.M41 -= Origin.X;
world.M42 -= Origin.Y;
world.M43 -= Origin.Z;
world.M41 -= (float)Origin.X;
world.M42 -= (float)Origin.Y;
world.M43 -= (float)Origin.Z;
}
};
@@ -33,6 +33,14 @@
#define VULKAN_USE_DEBUG_LAYER GPU_ENABLE_DIAGNOSTICS
#define VULKAN_USE_DEBUG_DATA (GPU_ENABLE_DIAGNOSTICS && COMPILE_WITH_DEV_ENV)
#ifndef VULKAN_USE_VALIDATION_CACHE
#ifdef VK_EXT_validation_cache
#define VULKAN_USE_VALIDATION_CACHE VK_EXT_validation_cache
#else
#define VULKAN_USE_VALIDATION_CACHE 0
#endif
#endif
#ifndef VULKAN_USE_QUERIES
#define VULKAN_USE_QUERIES 1
#endif
@@ -39,7 +39,7 @@ static const char* GInstanceExtensions[] =
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
#endif
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
VK_EXT_VALIDATION_CACHE_EXTENSION_NAME,
#endif
#if defined(VK_KHR_display) && 0
@@ -57,7 +57,7 @@ static const char* GDeviceExtensions[] =
#if VK_KHR_maintenance1
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
#endif
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
VK_EXT_VALIDATION_CACHE_EXTENSION_NAME,
#endif
#if VK_KHR_sampler_mirror_clamp_to_edge
@@ -582,7 +582,7 @@ void GPUDeviceVulkan::ParseOptionalDeviceExtensions(const Array<const char*>& de
OptionalDeviceExtensions.HasKHRMaintenance2 = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_MAINTENANCE2_EXTENSION_NAME);
#endif
OptionalDeviceExtensions.HasMirrorClampToEdge = RenderToolsVulkan::HasExtension(deviceExtensions, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME);
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
OptionalDeviceExtensions.HasEXTValidationCache = RenderToolsVulkan::HasExtension(deviceExtensions, VK_EXT_VALIDATION_CACHE_EXTENSION_NAME);
#endif
}
@@ -1439,7 +1439,7 @@ bool GPUDeviceVulkan::SavePipelineCache()
return File::WriteAllBytes(path, data);
}
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
void GetValidationCachePath(String& path)
{
@@ -1900,7 +1900,7 @@ bool GPUDeviceVulkan::Init()
const VkResult result = vkCreatePipelineCache(Device, &pipelineCacheCreateInfo, nullptr, &PipelineCache);
LOG_VULKAN_RESULT(result);
}
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
if (OptionalDeviceExtensions.HasEXTValidationCache && vkCreateValidationCacheEXT && vkDestroyValidationCacheEXT)
{
Array<uint8> data;
@@ -1915,16 +1915,16 @@ bool GPUDeviceVulkan::Init()
int32* dataPtr = (int32*)data.Get();
if (*dataPtr > 0)
{
dataPtr++;
const int32 version = *dataPtr++;
const int32 versionExpected = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
if (version == versionExpected)
const int32 cacheSize = *dataPtr++;
const int32 cacheVersion = *dataPtr++;
const int32 cacheVersionExpected = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
if (cacheVersion == cacheVersionExpected)
{
dataPtr += VK_UUID_SIZE / sizeof(int32);
}
else
{
LOG(Warning, "Bad validation cache file, version: {0}, expected: {1}", version, versionExpected);
LOG(Warning, "Bad validation cache file, version: {0}, expected: {1}", cacheVersion, cacheVersionExpected);
data.Clear();
}
}
@@ -2003,7 +2003,7 @@ void GPUDeviceVulkan::Dispose()
vkDestroyPipelineCache(Device, PipelineCache, nullptr);
PipelineCache = VK_NULL_HANDLE;
}
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
if (ValidationCache != VK_NULL_HANDLE)
{
if (SaveValidationCache())
@@ -400,7 +400,9 @@ public:
uint32 HasKHRMaintenance1 : 1;
uint32 HasKHRMaintenance2 : 1;
uint32 HasMirrorClampToEdge : 1;
#if VULKAN_USE_VALIDATION_CACHE
uint32 HasEXTValidationCache : 1;
#endif
};
static void GetInstanceLayersAndExtensions(Array<const char*>& outInstanceExtensions, Array<const char*>& outInstanceLayers, bool& outDebugUtils);
@@ -496,13 +498,11 @@ public:
/// </summary>
VkPipelineCache PipelineCache = VK_NULL_HANDLE;
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
/// <summary>
/// The optional validation cache.
/// </summary>
VkValidationCacheEXT ValidationCache = VK_NULL_HANDLE;
#endif
/// <summary>
@@ -584,12 +584,10 @@ public:
bool SavePipelineCache();
#if VK_EXT_validation_cache
/// <summary>
/// Saves the validation cache.
/// </summary>
bool SaveValidationCache();
#endif
private:
@@ -116,7 +116,7 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO);
createInfo.codeSize = (size_t)spirv.Length();
createInfo.pCode = (const uint32_t*)spirv.Get();
#if VK_EXT_validation_cache
#if VULKAN_USE_VALIDATION_CACHE
VkShaderModuleValidationCacheCreateInfoEXT validationInfo;
if (_device->ValidationCache != VK_NULL_HANDLE)
{
@@ -14,6 +14,7 @@
#include <vulkan/vulkan.h>
#undef VK_EXT_debug_utils
#undef VK_EXT_validation_cache
#define VULKAN_USE_VALIDATION_CACHE 0
#pragma clang diagnostic ignored "-Wpointer-bool-conversion"
#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
@@ -9,6 +9,9 @@
// Support more backbuffers in case driver decides to use more (https://gitlab.freedesktop.org/apinheiro/mesa/-/issues/9)
#define VULKAN_BACK_BUFFERS_COUNT_MAX 8
// Prevent wierd error 'Invalid VkValidationCacheEXT Object'
#define VULKAN_USE_VALIDATION_CACHE 0
/// <summary>
/// The implementation for the Vulkan API support for Linux platform.
/// </summary>
+11
View File
@@ -609,7 +609,10 @@ void Actor::SetIsActive(bool value)
void Actor::SetStaticFlags(StaticFlags value)
{
if (_staticFlags == value)
return;
_staticFlags = value;
OnStaticFlagsChanged();
}
void Actor::SetTransform(const Transform& value)
@@ -1226,6 +1229,14 @@ void Actor::OnOrderInParentChanged()
Level::callActorEvent(Level::ActorEventType::OnActorOrderInParentChanged, this, nullptr);
}
void Actor::OnStaticFlagsChanged()
{
}
void Actor::OnLayerChanged()
{
}
BoundingBox Actor::GetBoxWithChildren() const
{
BoundingBox result = GetBox();
+9 -10
View File
@@ -886,14 +886,12 @@ public:
/// Gets rotation of the actor oriented towards the specified world position with upwards direction.
/// </summary>
/// <param name="worldPos">The world position to orient towards.</param>
/// <param name="worldUp">The up direction that Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.</param>
/// <param name="worldUp">The up direction that constrains up axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.</param>
API_FUNCTION() Quaternion LookingAt(const Vector3& worldPos, const Vector3& worldUp) const;
public:
/// <summary>
/// Execute custom action on actors tree.
/// Action should returns false to stop calling deeper.
/// First action argument is current actor object.
/// </summary>
/// <param name="action">Actor to call on every actor in the tree. Returns true if keep calling deeper.</param>
/// <param name="args">Custom arguments for the function</param>
@@ -903,14 +901,12 @@ public:
if (action(this, args...))
{
for (int32 i = 0; i < Children.Count(); i++)
Children[i]->TreeExecute<Params...>(action, args...);
Children.Get()[i]->TreeExecute<Params...>(action, args...);
}
}
/// <summary>
/// Execute custom action on actor children tree.
/// Action should returns false to stop calling deeper.
/// First action argument is current actor object.
/// </summary>
/// <param name="action">Actor to call on every actor in the tree. Returns true if keep calling deeper.</param>
/// <param name="args">Custom arguments for the function</param>
@@ -918,7 +914,7 @@ public:
void TreeExecuteChildren(Function<bool(Actor*, Params ...)>& action, Params ... args)
{
for (int32 i = 0; i < Children.Count(); i++)
Children[i]->TreeExecute<Params...>(action, args...);
Children.Get()[i]->TreeExecute<Params...>(action, args...);
}
public:
@@ -1016,12 +1012,15 @@ public:
/// </summary>
virtual void OnOrderInParentChanged();
/// <summary>
/// Called when actor static flag gets changed.
/// </summary>
virtual void OnStaticFlagsChanged();
/// <summary>
/// Called when layer gets changed.
/// </summary>
virtual void OnLayerChanged()
{
}
virtual void OnLayerChanged();
/// <summary>
/// Called when adding object to the game.
+1 -1
View File
@@ -90,7 +90,7 @@ public:
/// <summary>
/// Gets the brush proxies per surface.
/// </summary>
API_PROPERTY(Attributes="Serialize, EditorOrder(100), EditorDisplay(\"Surfaces\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems = false, NotNullItems = true, ReadOnly = true)")
API_PROPERTY(Attributes="Serialize, EditorOrder(100), EditorDisplay(\"Surfaces\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems = false, NotNullItems = true, CanResize = true)")
Array<BrushSurface> GetSurfaces() const;
/// <summary>
+5
View File
@@ -349,6 +349,11 @@ void Camera::Draw(RenderContext& renderContext)
_previewModel->Draw(renderContext, draw);
}
}
// Load preview model if it doesnt exist. Ex: prefabs
else if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::EditorSprites) && !_previewModel)
{
_previewModel = Content::LoadAsyncInternal<Model>(TEXT("Editor/Camera/O_Camera"));
}
}
#include "Engine/Debug/DebugDraw.h"
@@ -41,7 +41,7 @@ public:
/// <summary>
/// Gets the model entries collection. Each entry contains data how to render meshes using this entry (transformation, material, shadows casting, etc.).
/// </summary>
API_PROPERTY(Attributes="Serialize, EditorOrder(1000), EditorDisplay(\"Entries\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems=false, NotNullItems=true, ReadOnly=true, Spacing=10)")
API_PROPERTY(Attributes="Serialize, EditorOrder(1000), EditorDisplay(\"Entries\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems=false, NotNullItems=true, CanResize=false, Spacing=10)")
FORCE_INLINE const Array<ModelInstanceEntry>& GetEntries() const
{
return Entries;
+1 -1
View File
@@ -33,7 +33,7 @@ void PostFxVolume::Collect(RenderContext& renderContext)
}
}
if (weight > ZeroTolerance)
if (weight > ZeroTolerance && renderContext.View.RenderLayersMask.HasLayer(GetLayer()))
{
const float totalSizeSqrt = (_transform.Scale * _size).LengthSquared();
renderContext.List->AddSettingsBlend((IPostFxSettingsProvider*)this, weight, _priority, totalSizeSqrt);
+1 -1
View File
@@ -99,7 +99,7 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M
DrawCall drawCall;
drawCall.World = world;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = _sphere.Radius;
drawCall.ObjectRadius = (float)_sphere.Radius;
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = GetPerInstanceRandom();
+1 -1
View File
@@ -410,7 +410,7 @@ void SplineModel::Draw(RenderContext& renderContext)
const Transform splineTransform = GetTransform();
renderContext.View.GetWorldMatrix(splineTransform, drawCall.World);
drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation();
drawCall.ObjectRadius = _sphere.Radius; // TODO: use radius for the spline chunk rather than whole spline
drawCall.ObjectRadius = (float)_sphere.Radius; // TODO: use radius for the spline chunk rather than whole spline
const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant();
for (int32 segment = 0; segment < _instances.Count(); segment++)
{
+7
View File
@@ -30,6 +30,12 @@ bool SceneAsset::IsInternalType() const
return true;
}
void SceneNavigation::Clear()
{
Volumes.Clear();
Actors.Clear();
}
BoundingBox SceneNavigation::GetNavigationBounds()
{
if (Volumes.IsEmpty())
@@ -372,6 +378,7 @@ void Scene::EndPlay()
// Improve scene cleanup performance by removing all data from scene rendering and ticking containers
Ticking.Clear();
Rendering.Clear();
Navigation.Clear();
// Base
Actor::EndPlay();
@@ -23,6 +23,17 @@ public:
/// </summary>
Array<NavMesh*> Meshes;
/// <summary>
/// The list of registered navigation-relevant actors (on the scene).
/// </summary>
Array<Actor*> Actors;
public:
/// <summary>
/// Clears this instance data.
/// </summary>
void Clear();
/// <summary>
/// Gets the total navigation volumes bounds.
/// </summary>
+3 -6
View File
@@ -18,7 +18,7 @@ class FLAXENGINE_API SceneQuery
{
public:
/// <summary>
/// Try to find actor hit by the given ray
/// Try to find actor hit by the given ray.
/// </summary>
/// <param name="ray">Ray to test</param>
/// <returns>Hit actor or nothing</returns>
@@ -55,19 +55,16 @@ public:
public:
/// <summary>
/// Execute custom action on actors tree.
/// Action should returns false to stop calling deeper.
/// First action argument is current actor object.
/// </summary>
/// <param name="action">Actor to call on every actor in the tree. Returns true if keep calling deeper.</param>
/// <param name="args">Custom arguments for the function</param>
template<typename... Params>
static void TreeExecute(Function<bool(Actor*, Params ...)>& action, Params ... args)
static void TreeExecute(Function<bool(Actor*, Params...)>& action, Params... args)
{
#if SCENE_QUERIES_WITH_LOCK
ScopeLock lock(Level::ScenesLock);
#endif
for (int32 i = 0; i < Level::Scenes.Count(); i++)
Level::Scenes[i]->TreeExecute<Params...>(action, args...);
Level::Scenes.Get()[i]->TreeExecute<Params...>(action, args...);
}
};
+15
View File
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "NavLink.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Serialization/Serialization.h"
NavLink::NavLink(const SpawnParams& params)
@@ -62,6 +63,20 @@ void NavLink::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
DESERIALIZE(BiDirectional);
}
void NavLink::OnEnable()
{
GetScene()->Navigation.Actors.Add(this);
Actor::OnEnable();
}
void NavLink::OnDisable()
{
Actor::OnDisable();
GetScene()->Navigation.Actors.Remove(this);
}
void NavLink::OnTransformChanged()
{
// Base
+2
View File
@@ -46,6 +46,8 @@ public:
#endif
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
void OnEnable() override;
void OnDisable() override;
protected:
// [Actor]
+159 -134
View File
@@ -23,7 +23,6 @@
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Level.h"
#include "Engine/Level/SceneQuery.h"
#include <ThirdParty/recastnavigation/Recast.h>
#include <ThirdParty/recastnavigation/DetourNavMeshBuilder.h>
#include <ThirdParty/recastnavigation/DetourNavMesh.h>
@@ -68,7 +67,7 @@ struct Modifier
NavAreaProperties* NavArea;
};
struct NavigationSceneRasterization
struct NavSceneRasterizer
{
NavMesh* NavMesh;
BoundingBox TileBoundsNavMesh;
@@ -83,7 +82,7 @@ struct NavigationSceneRasterization
Array<Modifier>* Modifiers;
const bool IsWorldToNavMeshIdentity;
NavigationSceneRasterization(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks, Array<Modifier>* modifiers)
NavSceneRasterizer(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks, Array<Modifier>* modifiers)
: TileBoundsNavMesh(tileBoundsNavMesh)
, WorldToNavMesh(worldToNavMesh)
, IsWorldToNavMeshIdentity(worldToNavMesh.IsIdentity())
@@ -103,35 +102,20 @@ struct NavigationSceneRasterization
auto& ib = IndexBuffer;
if (vb.IsEmpty() || ib.IsEmpty())
return;
PROFILE_CPU();
// Rasterize triangles
const Float3* vbData = vb.Get();
const int32* ibData = ib.Get();
Float3 v0, v1, v2;
if (IsWorldToNavMeshIdentity)
{
// Faster path
for (int32 i0 = 0; i0 < ib.Count();)
{
auto v0 = vb[ib[i0++]];
auto v1 = vb[ib[i0++]];
auto v2 = vb[ib[i0++]];
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true);
#endif
auto n = Float3::Cross(v0 - v1, v0 - v2);
n.Normalize();
const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : 0;
rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield);
}
}
else
{
// Transform vertices from world space into the navmesh space
const Matrix worldToNavMesh = WorldToNavMesh;
for (int32 i0 = 0; i0 < ib.Count();)
{
auto v0 = Float3::Transform(vb[ib[i0++]], worldToNavMesh);
auto v1 = Float3::Transform(vb[ib[i0++]], worldToNavMesh);
auto v2 = Float3::Transform(vb[ib[i0++]], worldToNavMesh);
v0 = vbData[ibData[i0++]];
v1 = vbData[ibData[i0++]];
v2 = vbData[ibData[i0++]];
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true);
#endif
@@ -142,6 +126,29 @@ struct NavigationSceneRasterization
rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield);
}
}
else
{
// Transform vertices from world space into the navmesh space
const Matrix worldToNavMesh = WorldToNavMesh;
for (int32 i0 = 0; i0 < ib.Count();)
{
Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v0);
Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v1);
Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v2);
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true);
#endif
auto n = Float3::Cross(v0 - v1, v0 - v2);
n.Normalize();
const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : RC_NULL_AREA;
rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield);
}
}
// Clear after use
vb.Clear();
ib.Clear();
}
static void TriangulateBox(Array<Float3>& vb, Array<int32>& ib, const OrientedBoundingBox& box)
@@ -215,88 +222,67 @@ struct NavigationSceneRasterization
}
}
static bool Walk(Actor* actor, NavigationSceneRasterization& e)
void Rasterize(Actor* actor)
{
// Early out if object is not intersecting with the tile bounds or is not using navigation
if (!actor->GetIsActive() || !(actor->GetStaticFlags() & StaticFlags::Navigation))
return true;
BoundingBox actorBoxNavMesh;
BoundingBox::Transform(actor->GetBox(), e.WorldToNavMesh, actorBoxNavMesh);
if (!actorBoxNavMesh.Intersects(e.TileBoundsNavMesh))
return true;
// Prepare buffers (for triangles)
auto& vb = e.VertexBuffer;
auto& ib = e.IndexBuffer;
vb.Clear();
ib.Clear();
// Extract data from the actor
if (const auto* boxCollider = dynamic_cast<BoxCollider*>(actor))
{
if (boxCollider->GetIsTrigger())
return true;
return;
PROFILE_CPU_NAMED("BoxCollider");
const OrientedBoundingBox box = boxCollider->GetOrientedBox();
TriangulateBox(vb, ib, box);
e.RasterizeTriangles();
TriangulateBox(VertexBuffer, IndexBuffer, box);
RasterizeTriangles();
}
else if (const auto* sphereCollider = dynamic_cast<SphereCollider*>(actor))
{
if (sphereCollider->GetIsTrigger())
return true;
return;
PROFILE_CPU_NAMED("SphereCollider");
const BoundingSphere sphere = sphereCollider->GetSphere();
TriangulateSphere(vb, ib, sphere);
e.RasterizeTriangles();
TriangulateSphere(VertexBuffer, IndexBuffer, sphere);
RasterizeTriangles();
}
else if (const auto* capsuleCollider = dynamic_cast<CapsuleCollider*>(actor))
{
if (capsuleCollider->GetIsTrigger())
return true;
return;
PROFILE_CPU_NAMED("CapsuleCollider");
const BoundingBox box = capsuleCollider->GetBox();
TriangulateBox(vb, ib, box);
e.RasterizeTriangles();
TriangulateBox(VertexBuffer, IndexBuffer, box);
RasterizeTriangles();
}
else if (const auto* meshCollider = dynamic_cast<MeshCollider*>(actor))
{
if (meshCollider->GetIsTrigger())
return true;
return;
PROFILE_CPU_NAMED("MeshCollider");
auto collisionData = meshCollider->CollisionData.Get();
if (!collisionData || collisionData->WaitForLoaded())
return true;
collisionData->ExtractGeometry(vb, ib);
return;
collisionData->ExtractGeometry(VertexBuffer, IndexBuffer);
Matrix meshColliderToWorld;
meshCollider->GetLocalToWorldMatrix(meshColliderToWorld);
for (auto& v : vb)
for (auto& v : VertexBuffer)
Float3::Transform(v, meshColliderToWorld, v);
e.RasterizeTriangles();
RasterizeTriangles();
}
else if (const auto* splineCollider = dynamic_cast<SplineCollider*>(actor))
{
if (splineCollider->GetIsTrigger())
return true;
return;
PROFILE_CPU_NAMED("SplineCollider");
auto collisionData = splineCollider->CollisionData.Get();
if (!collisionData || collisionData->WaitForLoaded())
return true;
return;
splineCollider->ExtractGeometry(vb, ib);
e.RasterizeTriangles();
splineCollider->ExtractGeometry(VertexBuffer, IndexBuffer);
RasterizeTriangles();
}
else if (const auto* terrain = dynamic_cast<Terrain*>(actor))
{
@@ -306,13 +292,13 @@ struct NavigationSceneRasterization
{
const auto patch = terrain->GetPatch(patchIndex);
BoundingBox patchBoundsNavMesh;
BoundingBox::Transform(patch->GetBounds(), e.WorldToNavMesh, patchBoundsNavMesh);
if (!patchBoundsNavMesh.Intersects(e.TileBoundsNavMesh))
BoundingBox::Transform(patch->GetBounds(), WorldToNavMesh, patchBoundsNavMesh);
if (!patchBoundsNavMesh.Intersects(TileBoundsNavMesh))
continue;
patch->ExtractCollisionGeometry(vb, ib);
e.RasterizeTriangles();
// TODO: get collision only from tile area
patch->ExtractCollisionGeometry(VertexBuffer, IndexBuffer);
RasterizeTriangles();
}
}
else if (const auto* navLink = dynamic_cast<NavLink*>(actor))
@@ -321,44 +307,33 @@ struct NavigationSceneRasterization
OffMeshLink link;
link.Start = navLink->GetTransform().LocalToWorld(navLink->Start);
Float3::Transform(link.Start, e.WorldToNavMesh, link.Start);
Float3::Transform(link.Start, WorldToNavMesh, link.Start);
link.End = navLink->GetTransform().LocalToWorld(navLink->End);
Float3::Transform(link.End, e.WorldToNavMesh, link.End);
Float3::Transform(link.End, WorldToNavMesh, link.End);
link.Radius = navLink->Radius;
link.BiDir = navLink->BiDirectional;
link.Id = GetHash(navLink->GetID());
e.OffMeshLinks->Add(link);
OffMeshLinks->Add(link);
}
else if (const auto* navModifierVolume = dynamic_cast<NavModifierVolume*>(actor))
{
if (navModifierVolume->AgentsMask.IsNavMeshSupported(e.NavMesh->Properties))
if (navModifierVolume->AgentsMask.IsNavMeshSupported(NavMesh->Properties))
{
PROFILE_CPU_NAMED("NavModifierVolume");
Modifier modifier;
OrientedBoundingBox bounds = navModifierVolume->GetOrientedBox();
bounds.Transform(e.WorldToNavMesh);
bounds.Transform(WorldToNavMesh);
bounds.GetBoundingBox(modifier.Bounds);
modifier.NavArea = navModifierVolume->GetNavArea();
e.Modifiers->Add(modifier);
Modifiers->Add(modifier);
}
}
return true;
}
};
void RasterizeGeometry(NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks, Array<Modifier>* modifiers)
{
PROFILE_CPU_NAMED("RasterizeGeometry");
NavigationSceneRasterization rasterization(navMesh, tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks, modifiers);
Function<bool(Actor*, NavigationSceneRasterization&)> treeWalkFunction(NavigationSceneRasterization::Walk);
SceneQuery::TreeExecute<NavigationSceneRasterization&>(treeWalkFunction, rasterization);
}
// Builds navmesh tile bounds and check if there are any valid navmesh volumes at that tile location
// Returns true if tile is intersecting with any navmesh bounds volume actor - which means tile is in use
bool GetNavMeshTileBounds(Scene* scene, NavMesh* navMesh, int32 x, int32 y, float tileSize, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh)
@@ -455,11 +430,44 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
Array<OffMeshLink> offMeshLinks;
Array<Modifier> modifiers;
RasterizeGeometry(navMesh, tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks, &modifiers);
{
PROFILE_CPU_NAMED("RasterizeGeometry");
NavSceneRasterizer rasterizer(navMesh, tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks, &modifiers);
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield);
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield);
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, *heightfield);
// Collect actors to rasterize
Array<Actor*> actors;
{
PROFILE_CPU_NAMED("CollectActors");
ScopeLock lock(Level::ScenesLock);
for (Scene* scene : Level::Scenes)
{
for (Actor* actor : scene->Navigation.Actors)
{
BoundingBox actorBoxNavMesh;
BoundingBox::Transform(actor->GetBox(), rasterizer.WorldToNavMesh, actorBoxNavMesh);
if (actorBoxNavMesh.Intersects(rasterizer.TileBoundsNavMesh) &&
actor->IsActiveInHierarchy() &&
EnumHasAllFlags(actor->GetStaticFlags(), StaticFlags::Navigation))
{
actors.Add(actor);
}
}
}
}
// Rasterize actors
for (Actor* actor : actors)
{
rasterizer.Rasterize(actor);
}
}
{
PROFILE_CPU_NAMED("FilterHeightfield");
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield);
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield);
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, *heightfield);
}
rcCompactHeightfield* compactHeightfield = rcAllocCompactHeightfield();
if (!compactHeightfield)
@@ -467,39 +475,51 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory compact heightfield.");
return true;
}
if (!rcBuildCompactHeightfield(&context, config.walkableHeight, config.walkableClimb, *heightfield, *compactHeightfield))
{
LOG(Warning, "Could not generate navmesh: Could not build compact data.");
return true;
PROFILE_CPU_NAMED("CompactHeightfield");
if (!rcBuildCompactHeightfield(&context, config.walkableHeight, config.walkableClimb, *heightfield, *compactHeightfield))
{
LOG(Warning, "Could not generate navmesh: Could not build compact data.");
return true;
}
}
rcFreeHeightField(heightfield);
if (!rcErodeWalkableArea(&context, config.walkableRadius, *compactHeightfield))
{
LOG(Warning, "Could not generate navmesh: Could not erode.");
return true;
PROFILE_CPU_NAMED("ErodeWalkableArea");
if (!rcErodeWalkableArea(&context, config.walkableRadius, *compactHeightfield))
{
LOG(Warning, "Could not generate navmesh: Could not erode.");
return true;
}
}
// Mark areas
for (auto& modifier : modifiers)
{
const unsigned char areaId = modifier.NavArea ? modifier.NavArea->Id : RC_NULL_AREA;
Float3 bMin = modifier.Bounds.Minimum;
Float3 bMax = modifier.Bounds.Maximum;
rcMarkBoxArea(&context, &bMin.X, &bMax.X, areaId, *compactHeightfield);
PROFILE_CPU_NAMED("MarkModifiers");
for (auto& modifier : modifiers)
{
const unsigned char areaId = modifier.NavArea ? modifier.NavArea->Id : RC_NULL_AREA;
Float3 bMin = modifier.Bounds.Minimum;
Float3 bMax = modifier.Bounds.Maximum;
rcMarkBoxArea(&context, &bMin.X, &bMax.X, areaId, *compactHeightfield);
}
}
if (!rcBuildDistanceField(&context, *compactHeightfield))
{
LOG(Warning, "Could not generate navmesh: Could not build distance field.");
return true;
PROFILE_CPU_NAMED("BuildDistanceField");
if (!rcBuildDistanceField(&context, *compactHeightfield))
{
LOG(Warning, "Could not generate navmesh: Could not build distance field.");
return true;
}
}
if (!rcBuildRegions(&context, *compactHeightfield, config.borderSize, config.minRegionArea, config.mergeRegionArea))
{
LOG(Warning, "Could not generate navmesh: Could not build regions.");
return true;
PROFILE_CPU_NAMED("BuildRegions");
if (!rcBuildRegions(&context, *compactHeightfield, config.borderSize, config.minRegionArea, config.mergeRegionArea))
{
LOG(Warning, "Could not generate navmesh: Could not build regions.");
return true;
}
}
rcContourSet* contourSet = rcAllocContourSet();
@@ -508,10 +528,13 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory for contour set.");
return true;
}
if (!rcBuildContours(&context, *compactHeightfield, config.maxSimplificationError, config.maxEdgeLen, *contourSet))
{
LOG(Warning, "Could not generate navmesh: Could not create contours.");
return true;
PROFILE_CPU_NAMED("BuildContours");
if (!rcBuildContours(&context, *compactHeightfield, config.maxSimplificationError, config.maxEdgeLen, *contourSet))
{
LOG(Warning, "Could not generate navmesh: Could not create contours.");
return true;
}
}
rcPolyMesh* polyMesh = rcAllocPolyMesh();
@@ -520,10 +543,13 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory for poly mesh.");
return true;
}
if (!rcBuildPolyMesh(&context, *contourSet, config.maxVertsPerPoly, *polyMesh))
{
LOG(Warning, "Could not generate navmesh: Could not triangulate contours.");
return true;
PROFILE_CPU_NAMED("BuildPolyMesh");
if (!rcBuildPolyMesh(&context, *contourSet, config.maxVertsPerPoly, *polyMesh))
{
LOG(Warning, "Could not generate navmesh: Could not triangulate contours.");
return true;
}
}
rcPolyMeshDetail* detailMesh = rcAllocPolyMeshDetail();
@@ -532,20 +558,20 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory for detail mesh.");
return true;
}
if (!rcBuildPolyMeshDetail(&context, *polyMesh, *compactHeightfield, config.detailSampleDist, config.detailSampleMaxError, *detailMesh))
{
LOG(Warning, "Could not generate navmesh: Could not build detail mesh.");
return true;
PROFILE_CPU_NAMED("BuildPolyMeshDetail");
if (!rcBuildPolyMeshDetail(&context, *polyMesh, *compactHeightfield, config.detailSampleDist, config.detailSampleMaxError, *detailMesh))
{
LOG(Warning, "Could not generate navmesh: Could not build detail mesh.");
return true;
}
}
rcFreeCompactHeightfield(compactHeightfield);
rcFreeContourSet(contourSet);
for (int i = 0; i < polyMesh->npolys; i++)
{
polyMesh->flags[i] = polyMesh->areas[i] != RC_NULL_AREA ? 1 : 0;
}
if (polyMesh->nverts == 0)
{
// Empty tile
@@ -623,15 +649,18 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
// Generate navmesh tile data
unsigned char* navData = nullptr;
int navDataSize = 0;
if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
{
LOG(Warning, "Could not build Detour navmesh.");
return true;
PROFILE_CPU_NAMED("CreateNavMeshData");
if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
{
LOG(Warning, "Could not build Detour navmesh.");
return true;
}
}
ASSERT_LOW_LAYER(navDataSize > 4 && *(uint32*)navData == DT_NAVMESH_MAGIC); // Sanity check for Detour header
{
PROFILE_CPU_NAMED("Navigation.CreateTile");
PROFILE_CPU_NAMED("CreateTiles");
ScopeLock lock(runtime->Locker);
@@ -729,17 +758,13 @@ public:
bool Run() override
{
PROFILE_CPU_NAMED("BuildNavMeshTile");
const auto navMesh = NavMesh.Get();
if (!navMesh)
{
return false;
}
if (GenerateTile(NavMesh, Runtime, X, Y, TileBoundsNavMesh, WorldToNavMesh, TileSize, Config))
{
LOG(Warning, "Failed to generate navmesh tile at {0}x{1}.", X, Y);
}
return false;
}
@@ -776,7 +801,7 @@ void OnSceneUnloading(Scene* scene, const Guid& sceneId)
{
NavBuildTasksLocker.Unlock();
// Cancel task but without locking queue from this thread to prevent dead-locks
// Cancel task but without locking queue from this thread to prevent deadlocks
task->Cancel();
NavBuildTasksLocker.Lock();
@@ -815,7 +840,7 @@ float NavMeshBuilder::GetNavMeshBuildingProgress()
return result;
}
void BuildTileAsync(NavMesh* navMesh, int32 x, int32 y, rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize)
void BuildTileAsync(NavMesh* navMesh, const int32 x, const int32 y, const rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize)
{
NavMeshRuntime* runtime = navMesh->GetRuntime();
NavBuildTasksLocker.Lock();
@@ -1108,7 +1133,7 @@ void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float t
if (!scene)
{
LOG(Warning, "Could not generate navmesh without scene.");
return;
return;
}
// Early out if scene is not using navigation
@@ -47,6 +47,20 @@ void NavModifierVolume::Deserialize(DeserializeStream& stream, ISerializeModifie
DESERIALIZE(AreaName);
}
void NavModifierVolume::OnEnable()
{
GetScene()->Navigation.Actors.Add(this);
BoxVolume::OnEnable();
}
void NavModifierVolume::OnDisable()
{
BoxVolume::OnDisable();
GetScene()->Navigation.Actors.Remove(this);
}
void NavModifierVolume::OnBoundsChanged(const BoundingBox& prevBounds)
{
#if COMPILE_WITH_NAV_MESH_BUILDER
@@ -34,6 +34,8 @@ public:
// [BoxVolume]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
void OnEnable() override;
void OnDisable() override;
protected:
// [BoxVolume]
+25 -6
View File
@@ -1392,12 +1392,20 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
if (ownerClientId == NetworkManager::LocalClientId)
{
// Ensure local client owns that object actually
CHECK(localRole == NetworkObjectRole::OwnedAuthoritative);
if (localRole != NetworkObjectRole::OwnedAuthoritative)
{
LOG(Error, "Cannot change overship of object (Id={}) to the local client (Id={}) if the local role is not set to OwnedAuthoritative.", obj->GetID(), ownerClientId);
return;
}
}
else
{
// Ensure local client doesn't own that object since it's owned by other client
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
if (localRole == NetworkObjectRole::OwnedAuthoritative)
{
LOG(Error, "Cannot change overship of object (Id={}) to the remote client (Id={}) if the local role is set to OwnedAuthoritative.", obj->GetID(), ownerClientId);
return;
}
}
#endif
item.HasOwnership = true;
@@ -1421,7 +1429,13 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
if (item.OwnerClientId != ownerClientId)
{
// Change role locally
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
#if !BUILD_RELEASE
if (localRole == NetworkObjectRole::OwnedAuthoritative)
{
LOG(Error, "Cannot change overship of object (Id={}) to the remote client (Id={}) if the local role is set to OwnedAuthoritative.", obj->GetID(), ownerClientId);
return;
}
#endif
if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative)
Hierarchy->RemoveObject(obj);
item.OwnerClientId = ownerClientId;
@@ -1433,7 +1447,13 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli
else
{
// Allow to change local role of the object (except ownership)
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
#if !BUILD_RELEASE
if (localRole == NetworkObjectRole::OwnedAuthoritative)
{
LOG(Error, "Cannot change overship of object (Id={}) to the remote client (Id={}) if the local role is set to OwnedAuthoritative.", obj->GetID(), ownerClientId);
return;
}
#endif
if (Hierarchy && it->Item.Role == NetworkObjectRole::OwnedAuthoritative)
Hierarchy->RemoveObject(obj);
item.Role = localRole;
@@ -2211,7 +2231,6 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
if (!obj)
return;
// Validate RPC
if (info->Server && NetworkManager::IsClient())
{
@@ -2234,7 +2253,7 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
// Execute RPC
info->Execute(obj, stream, info->Tag);
}
else if(info->Channel != static_cast<uint8>(NetworkChannelType::Unreliable) && info->Channel != static_cast<uint8>(NetworkChannelType::UnreliableOrdered))
else if (info->Channel != static_cast<uint8>(NetworkChannelType::Unreliable) && info->Channel != static_cast<uint8>(NetworkChannelType::UnreliableOrdered))
{
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", msgData.ObjectId, String(msgData.RpcTypeName), String(msgData.RpcName));
}
+1 -1
View File
@@ -371,7 +371,7 @@ void ParticleEffect::Sync()
SceneRenderTask* ParticleEffect::GetRenderTask() const
{
const uint64 minFrame = Engine::UpdateCount - 2;
const uint64 minFrame = Engine::FrameCount - 2;
// Custom task
const auto customViewRenderTask = CustomViewRenderTask.Get();
+1 -1
View File
@@ -939,7 +939,7 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
DrawCall drawCall;
drawCall.PerInstanceRandom = effect->GetPerInstanceRandom();
drawCall.ObjectPosition = effect->GetSphere().Center - view.Origin;
drawCall.ObjectRadius = effect->GetSphere().Radius;
drawCall.ObjectRadius = (float)effect->GetSphere().Radius;
// Draw all emitters
for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++)
+31 -12
View File
@@ -2,6 +2,7 @@
#include "Collider.h"
#include "Engine/Core/Log.h"
#include "Engine/Level/Scene/Scene.h"
#if USE_EDITOR
#include "Engine/Level/Scene/SceneRendering.h"
#endif
@@ -35,6 +36,13 @@ void Collider::SetIsTrigger(bool value)
_isTrigger = value;
if (_shape)
PhysicsBackend::SetShapeState(_shape, IsActiveInHierarchy(), _isTrigger && CanBeTrigger());
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && _isEnabled)
{
if (_isTrigger)
GetScene()->Navigation.Actors.Remove(this);
else
GetScene()->Navigation.Actors.Add(this);
}
}
void Collider::SetCenter(const Vector3& value)
@@ -43,13 +51,9 @@ void Collider::SetCenter(const Vector3& value)
return;
_center = value;
if (_staticActor)
{
PhysicsBackend::SetShapeLocalPose(_shape, _center, Quaternion::Identity);
}
else if (const RigidBody* rigidBody = GetAttachedRigidBody())
{
PhysicsBackend::SetShapeLocalPose(_shape, (_localTransform.Translation + _localTransform.Orientation * _center) * rigidBody->GetScale(), _localTransform.Orientation);
}
UpdateBounds();
}
@@ -134,25 +138,27 @@ RigidBody* Collider::GetAttachedRigidBody() const
return nullptr;
}
#if USE_EDITOR
void Collider::OnEnable()
{
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger)
GetScene()->Navigation.Actors.Add(this);
#if USE_EDITOR
GetSceneRendering()->AddPhysicsDebug<Collider, &Collider::DrawPhysicsDebug>(this);
#endif
// Base
Actor::OnEnable();
PhysicsColliderActor::OnEnable();
}
void Collider::OnDisable()
{
// Base
Actor::OnDisable();
PhysicsColliderActor::OnDisable();
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger)
GetScene()->Navigation.Actors.Remove(this);
#if USE_EDITOR
GetSceneRendering()->RemovePhysicsDebug<Collider, &Collider::DrawPhysicsDebug>(this);
}
#endif
}
void Collider::Attach(RigidBody* rigidBody)
{
@@ -432,6 +438,19 @@ void Collider::OnLayerChanged()
UpdateLayerBits();
}
void Collider::OnStaticFlagsChanged()
{
PhysicsColliderActor::OnStaticFlagsChanged();
if (!_isTrigger && _isEnabled)
{
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation))
GetScene()->Navigation.Actors.AddUnique(this);
else
GetScene()->Navigation.Actors.Remove(this);
}
}
void Collider::OnPhysicsSceneChanged(PhysicsScene* previous)
{
PhysicsColliderActor::OnPhysicsSceneChanged(previous);
+1 -2
View File
@@ -171,15 +171,14 @@ public:
protected:
// [PhysicsColliderActor]
#if USE_EDITOR
void OnEnable() override;
void OnDisable() override;
#endif
void BeginPlay(SceneBeginData* data) override;
void EndPlay() override;
void OnActiveInTreeChanged() override;
void OnParentChanged() override;
void OnTransformChanged() override;
void OnLayerChanged() override;
void OnStaticFlagsChanged() override;
void OnPhysicsSceneChanged(PhysicsScene* previous) override;
};
+8 -5
View File
@@ -8,6 +8,7 @@
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/CollisionCooking.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Threading/Threading.h"
REGISTER_BINARY_ASSET(CollisionData, "FlaxEngine.CollisionData", true);
@@ -33,6 +34,7 @@ bool CollisionData::CookCollision(CollisionDataType type, ModelBase* modelObj, i
LOG(Error, "Cannot cook collision data for virtual models on a main thread (virtual models data is stored on GPU only). Use thread pool or async task.");
return true;
}
PROFILE_CPU();
// Prepare
CollisionCooking::Argument arg;
@@ -61,6 +63,7 @@ bool CollisionData::CookCollision(CollisionDataType type, ModelBase* modelObj, i
bool CollisionData::CookCollision(CollisionDataType type, const Span<Float3>& vertices, const Span<uint32>& triangles, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
PROFILE_CPU();
CHECK_RETURN(vertices.Length() != 0, true);
CHECK_RETURN(triangles.Length() != 0 && triangles.Length() % 3 == 0, true);
ModelData modelData;
@@ -74,6 +77,7 @@ bool CollisionData::CookCollision(CollisionDataType type, const Span<Float3>& ve
bool CollisionData::CookCollision(CollisionDataType type, const Span<Float3>& vertices, const Span<int32>& triangles, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
PROFILE_CPU();
CHECK_RETURN(vertices.Length() != 0, true);
CHECK_RETURN(triangles.Length() != 0 && triangles.Length() % 3 == 0, true);
ModelData modelData;
@@ -89,12 +93,12 @@ bool CollisionData::CookCollision(CollisionDataType type, const Span<Float3>& ve
bool CollisionData::CookCollision(CollisionDataType type, ModelData* modelData, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
// Validate state
if (!IsVirtual())
{
LOG(Warning, "Only virtual assets can be modified at runtime.");
return true;
}
PROFILE_CPU();
// Prepare
CollisionCooking::Argument arg;
@@ -107,18 +111,14 @@ bool CollisionData::CookCollision(CollisionDataType type, ModelData* modelData,
SerializedOptions options;
BytesContainer outputData;
if (CollisionCooking::CookCollision(arg, options, outputData))
{
return true;
}
// Clear state
unload(true);
// Load data
if (load(&options, outputData.Get(), outputData.Length()) != LoadResult::Ok)
{
return true;
}
// Mark as loaded (eg. Mesh Colliders using this asset will update shape for physics simulation)
onLoaded();
@@ -133,6 +133,7 @@ bool CollisionData::GetModelTriangle(uint32 faceIndex, MeshBase*& mesh, uint32&
meshTriangleIndex = MAX_uint32;
if (!IsLoaded())
return false;
PROFILE_CPU();
ScopeLock lock(Locker);
if (_triangleMesh)
{
@@ -178,6 +179,7 @@ bool CollisionData::GetModelTriangle(uint32 faceIndex, MeshBase*& mesh, uint32&
void CollisionData::ExtractGeometry(Array<Float3>& vertexBuffer, Array<int32>& indexBuffer) const
{
PROFILE_CPU();
vertexBuffer.Clear();
indexBuffer.Clear();
@@ -194,6 +196,7 @@ const Array<Float3>& CollisionData::GetDebugLines()
{
if (_hasMissingDebugLines && IsLoaded())
{
PROFILE_CPU();
ScopeLock lock(Locker);
_hasMissingDebugLines = false;
@@ -4265,6 +4265,7 @@ void* PhysicsBackend::CreateHeightField(byte* data, int32 dataSize)
void PhysicsBackend::GetConvexMeshTriangles(void* contextMesh, Array<Float3, HeapAllocation>& vertexBuffer, Array<int, HeapAllocation>& indexBuffer)
{
PROFILE_CPU();
auto contextMeshPhysX = (PxConvexMesh*)contextMesh;
uint32 numIndices = 0;
uint32 numVertices = contextMeshPhysX->getNbVertices();
@@ -4304,6 +4305,7 @@ void PhysicsBackend::GetConvexMeshTriangles(void* contextMesh, Array<Float3, Hea
void PhysicsBackend::GetTriangleMeshTriangles(void* triangleMesh, Array<Float3>& vertexBuffer, Array<int32, HeapAllocation>& indexBuffer)
{
PROFILE_CPU();
auto triangleMeshPhysX = (PxTriangleMesh*)triangleMesh;
uint32 numVertices = triangleMeshPhysX->getNbVertices();
uint32 numIndices = triangleMeshPhysX->getNbTriangles() * 3;
@@ -770,14 +770,24 @@ void WindowsWindow::CheckForWindowResize()
}
}
void WindowsWindow::UpdateCursor() const
void WindowsWindow::UpdateCursor()
{
// Don't hide cursor when window is not focused
if (_cursor == CursorType::Hidden && _focused)
{
if (!_lastCursorHidden)
{
_lastCursorHidden = true;
::ShowCursor(FALSE);
}
::SetCursor(nullptr);
return;
}
else if (_lastCursorHidden)
{
_lastCursorHidden = false;
::ShowCursor(TRUE);
}
int32 index = 0;
switch (_cursor)
@@ -28,6 +28,7 @@ private:
bool _isSwitchingFullScreen = false;
bool _trackingMouse = false;
bool _clipCursorSet = false;
bool _lastCursorHidden = false;
bool _isDuringMaximize = false;
Windows::HANDLE _monitor = nullptr;
Windows::LONG _clipCursorRect[4];
@@ -90,7 +91,7 @@ public:
private:
void CheckForWindowResize();
void UpdateCursor() const;
void UpdateCursor();
void UpdateRegion();
public:
+2 -2
View File
@@ -155,9 +155,9 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry)
// Get the index to the glyph in the font face
const FT_UInt glyphIndex = FT_Get_Char_Index(face, c);
#if !BUILD_RELEASE
if (glyphIndex == 0)
if (glyphIndex == 0 && c >= '!')
{
LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c);
LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font.", String(face->family_name), c);
}
#endif
+1 -1
View File
@@ -1175,7 +1175,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
drawCall.AsChar.Mat = nullptr;
}
Float2 pointer = location;
for (int32 currentIndex = 0; currentIndex <= text.Length(); currentIndex++)
for (int32 currentIndex = 0; currentIndex < text.Length(); currentIndex++)
{
// Cache current character
const Char currentChar = text[currentIndex];
+1 -1
View File
@@ -464,7 +464,7 @@ void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* light
transform.Scale *= decal->GetSize();
renderContext.View.GetWorldMatrix(transform, drawCall.World);
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = decal->GetSphere().Radius;
drawCall.ObjectRadius = (float)decal->GetSphere().Radius;
context->ResetRenderTarget();
+32 -1
View File
@@ -334,6 +334,32 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
}
setup.UseTemporalAAJitter = aaMode == AntialiasingMode::TemporalAntialiasing;
// Disable TAA jitter in debug modes
switch (renderContext.View.Mode)
{
case ViewMode::Unlit:
case ViewMode::Diffuse:
case ViewMode::Normals:
case ViewMode::Depth:
case ViewMode::Emissive:
case ViewMode::AmbientOcclusion:
case ViewMode::Metalness:
case ViewMode::Roughness:
case ViewMode::Specular:
case ViewMode::SpecularColor:
case ViewMode::SubsurfaceColor:
case ViewMode::ShadingModel:
case ViewMode::Reflections:
case ViewMode::GlobalSDF:
case ViewMode::GlobalSurfaceAtlas:
case ViewMode::LightmapUVsDensity:
case ViewMode::MaterialComplexity:
case ViewMode::Wireframe:
case ViewMode::NoPostFx:
setup.UseTemporalAAJitter = false;
break;
}
// Customize setup (by postfx or custom gameplay effects)
renderContext.Task->SetupRender(renderContext);
for (PostProcessEffect* e : renderContext.List->PostFx)
@@ -500,8 +526,13 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
EyeAdaptationPass::Instance()->Render(renderContext, lightBuffer);
PostProcessingPass::Instance()->Render(renderContext, lightBuffer, tempBuffer, colorGradingLUT);
RenderTargetPool::Release(colorGradingLUT);
RenderTargetPool::Release(lightBuffer);
context->ResetRenderTarget();
if (aaMode == AntialiasingMode::TemporalAntialiasing)
{
TAA::Instance()->Render(renderContext, tempBuffer, lightBuffer->View());
Swap(lightBuffer, tempBuffer);
}
RenderTargetPool::Release(lightBuffer);
context->SetRenderTarget(task->GetOutputView());
context->SetViewportAndScissors(task->GetOutputViewport());
context->Draw(tempBuffer);
@@ -46,6 +46,11 @@ namespace FlaxEngine
/// </summary>
public bool CanReorderItems = true;
/// <summary>
/// Gets or sets whether items can be added or removed from this collection.
/// </summary>
public bool CanResize = true;
/// <summary>
/// Gets or sets whether the items of this collection can be null. If <c>true</c>, applications using this collection should prevent user to add null items to the collection.
/// </summary>
@@ -23,11 +23,14 @@ namespace FlaxEngine
public int FontSize;
/// <summary>
/// The custom header color (as 32-bit uint).
/// The custom header color (as 32-bit uint in RGB order, bottom bits contain Blue).
/// </summary>
public uint Color;
private HeaderAttribute()
/// <summary>
/// Initializes a new instance of the <see cref="HeaderAttribute"/> class.
/// </summary>
public HeaderAttribute()
{
}
@@ -0,0 +1,42 @@
using System;
namespace FlaxEngine;
/// <summary>
/// Used to add a watermark to a string textbox in the editor field
/// </summary>
[Serializable]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class WatermarkAttribute : Attribute
{
/// <summary>
/// The watermark text.
/// </summary>
public string WatermarkText;
/// <summary>
/// The watermark color (as 32-bit uint in RGB order, bottom bits contain Blue).
/// </summary>
public uint WatermarkColor;
/// <summary>
/// Initializes a new instance of the <see cref="WatermarkAttribute"/> class.
/// </summary>
/// <param name="text">The watermark text.</param>
public WatermarkAttribute(string text)
{
WatermarkText = text;
WatermarkColor = 0; // Default color of watermark in textbox
}
/// <summary>
/// Initializes a new instance of the <see cref="WatermarkAttribute"/> class.
/// </summary>
/// <param name="text">The watermark text.</param>
/// <param name="color">The watermark color. 0 to use default.</param>
public WatermarkAttribute(string text, uint color)
{
WatermarkText = text;
WatermarkColor = color;
}
}

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