diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 428dea933..436850e80 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,12 +1,13 @@ name: Continuous Deployment on: schedule: - - cron: '15 4 * * *' + - cron: '15 6 * * *' workflow_dispatch: env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: false + GIT_LFS_PULL_OPTIONS: '-c lfs.concurrenttransfers=1 -c lfs.transfer.maxretries=2 -c http.version="HTTP/1.1" -c lfs.activitytimeout=60' jobs: @@ -20,7 +21,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -35,12 +36,12 @@ jobs: run: | .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Windows-Editor path: Output/Editor.zip - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Windows-EditorDebugSymbols path: Output/EditorDebugSymbols.zip @@ -53,7 +54,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -68,7 +69,7 @@ jobs: run: | .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Windows-Game path: Output/Windows.zip @@ -83,7 +84,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Install dependencies run: | sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev @@ -101,7 +102,7 @@ jobs: run: | ./PackageEditor.sh -arch=x64 -platform=Linux -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Linux-Editor path: Output/FlaxEditorLinux.zip @@ -114,7 +115,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Install dependencies run: | sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev @@ -132,7 +133,7 @@ jobs: run: | ./PackagePlatforms.sh -arch=x64 -platform=Linux -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Linux-Game path: Output/Linux.zip @@ -147,7 +148,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -162,7 +163,7 @@ jobs: run: | ./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Mac-Editor path: Output/FlaxEditorMac.zip @@ -175,7 +176,7 @@ jobs: - name: Checkout LFS run: | git lfs version - git lfs pull + git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -190,7 +191,7 @@ jobs: run: | ./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Mac-Game path: Output/Mac.zip diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index 81921e0a9..ef459f7b7 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d2e306ad841a731dd9beced0fd653ff9649403e30545d5e31eed9b3f575513d +oid sha256:3aad926ae11ee4b172c227e47f611090d99285ce1d4e3ea73b21f66e6653ac15 size 28071 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index b1dd13160..1f883d068 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9618fe8e4e7673a8fe9c51393c00ff8028fd48c5433b2982d8368832f919dbbb +oid sha256:9d6a0fada8ef3a5a6884150621d48a30c23a0d94f2e114fb0e306361b7aeb079 size 29786 diff --git a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax index a1b73ca44..c2de0c98a 100644 --- a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax +++ b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2016b2a5f524d0c5610539108b925c784c7d56eb18b050f78a0805150a90a5b2 +oid sha256:08c34225a9bc0352ee74811905978489263cc9eb6a65845717273ab997bdafaf size 39019 diff --git a/Content/Editor/DebugMaterials/SingleColor/Decal.flax b/Content/Editor/DebugMaterials/SingleColor/Decal.flax index 04300ddb5..c99a14f6c 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Decal.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Decal.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef3e97169279f1484bdb28d913587058d526172457878f86f8c54634c4c1b1cd +oid sha256:a69bfa758716071d2c6621cca6d049904a9f96d16b41ed90705d7f262be2cdab size 7489 diff --git a/Content/Editor/DebugMaterials/SingleColor/Particle.flax b/Content/Editor/DebugMaterials/SingleColor/Particle.flax index 99eb3da18..156186cb8 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Particle.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Particle.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af65802e9b6437c3b3d1f0cbf0c320fde5d9774acea14bdf374a8b0dd28d5610 +oid sha256:126ea5a12927d82b6c7a0cd1e8cf14d46d591b4de31088626a50a54232cfa289 size 32168 diff --git a/Content/Editor/DebugMaterials/SingleColor/Surface.flax b/Content/Editor/DebugMaterials/SingleColor/Surface.flax index 569144f73..91306475f 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Surface.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Surface.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e26ff8a13ba0613915fa52ede6620a922e97d8e1f1bed9fae23b446178801929 +oid sha256:0daadb37a636e2b895098abb741f7563d1a8d52f60613272bfabd4ccbd3a1a6a size 27967 diff --git a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax index 04eb52e4c..a8b73fe93 100644 --- a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax +++ b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74078179b7b49c50718135028852415829c2202a2f45e057407f889c0e638047 +oid sha256:4fab658ed8bb4d251c73303f027b90acf9196a3fc9e4cd5d74ab076aec4336ec size 30152 diff --git a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax index 2625a8a6b..11dc9cedf 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07d8d36958fc592217a7999b6864bb9885e0a27d718d36d9216c16701fa124bc -size 21314 +oid sha256:8ce53c8358889b333381d5f1c0e97cb210e8bec60536343f306095015251e70c +size 21361 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index 3c9304b8e..b03a8e029 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83a576dd776ba57c7d8ee5947438328acde6fab63442787d8411ee5377db0920 +oid sha256:a7127c5615f59da6e99298fddcbbec663c16ed0e7b51e06273746febca8084b5 size 28146 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index bcc7272d5..4525080ca 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59006b776f59d1445758fd5e04892583d85506bf6b7312d15613d79975886932 +oid sha256:b5e8b335a0de3af9495e6838bc033b7452e525c54d1ad27f779f9313d218ad22 size 36179 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index 872b529e3..a00d8f2fe 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af4271107789ee629c6b5f2f0d4d2324a0a8b124043f6dd14f977514c408014b +oid sha256:417bc90194e62cbb2ee094c0ae98e20c2cd912331e7ff2da6a99436cd7c44ecb size 31306 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index 0f93a951d..7d6df0fc9 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fa1233fa8f24ad42190b127cb4fc9aeb5832e188e27151f95a88614881463ee +oid sha256:991b76c5a6a0acfe0364960516211e0512a361882b09fa297daf31d85e41f1e5 size 29894 diff --git a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax index 5cde31ea7..a8396c3f2 100644 --- a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax +++ b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c53438a299dbf8d0eced6a47477470e0263ccbe40acbaa62a5f684eea0e0f112 +oid sha256:51d9d5bd33e7e95181a1b26337d5c9702362659b0ca9544efd3d5fa101856a73 size 16166 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index d7e89b477..2e5c0df28 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c8a8eda823e4cf72cc8889a4f448d1d25c0564131ade142c552e3dba0dc36a8 +oid sha256:5a931f969cda4d2efbc33321e8196155460d90e1aa95fdba9c59aa5919602962 size 29080 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index 6a8c10a52..c5f79236d 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0da04f087f99fdb90a3bb4baac3400761048953d659964d108513bfa4303084a +oid sha256:de1d24816df35ad746eb9efa28823d182f341a1d0bdd6b0f15db1030d531cbbb size 28549 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index d5d64147e..0c99f405d 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6733e421934f43bb8149e78b3be2b28fd14920e0580ea6333aee698cd3bbc303 +oid sha256:63b4dd757f757a5b0ecb8ce41d4726717155963eee9e877f2731a98054100343 size 28477 diff --git a/Content/Editor/IesProfilePreviewMaterial.flax b/Content/Editor/IesProfilePreviewMaterial.flax index fc0d20df9..8a59e01f6 100644 --- a/Content/Editor/IesProfilePreviewMaterial.flax +++ b/Content/Editor/IesProfilePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c473ea456efdce9c3e916bd95c6c286a13646e413aa17f1569f7bd51c811358 +oid sha256:dc1e903d13905455ec7c6dfb04bd0920093e0fdd57c7243810c0c9ef7177df7d size 18205 diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index 45b6918dd..eec4de8cf 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c76a3d52b95cd64d95f3db343b05aec2a66c59f0317fb3967c50240db8af0e22 +oid sha256:d38e7a4313eb72bda5cc69fd17f119323587a848c09b1e50e1784281070fe2ae size 30407 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index c32380913..103af7304 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94d4ec85842448736c087594f30df7a5f6fe33a16c2cc59781f36adebdbc0b9a -size 39167 +oid sha256:25a5f3db011be73c56320f2b95501536161b60c7ba3c997b8f50633348b90a24 +size 38555 diff --git a/Content/Editor/Scripting/GamePluginTemplate.cs b/Content/Editor/Scripting/GamePluginTemplate.cs new file mode 100644 index 000000000..d110fa68d --- /dev/null +++ b/Content/Editor/Scripting/GamePluginTemplate.cs @@ -0,0 +1,25 @@ +%copyright%using System; +using System.Collections.Generic; +using FlaxEngine; + +namespace %namespace%; + +/// +/// %class% GamePlugin. +/// +public class %class% : GamePlugin +{ + /// + public override void Initialize() + { + base.Initialize(); + + } + + /// + public override void Deinitialize() + { + base.Deinitialize(); + + } +} \ No newline at end of file diff --git a/Content/Editor/SpriteMaterial.flax b/Content/Editor/SpriteMaterial.flax index df5d7fd21..871c6b907 100644 --- a/Content/Editor/SpriteMaterial.flax +++ b/Content/Editor/SpriteMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e76b14ab9f0e53b0d50e24b44a7d3406f156bcdca64f154f1c1d92f8efaa6cf +oid sha256:89c4af798dc4a6790aaac0ee11e7f45511a30bd561edfd1d86502b6bf68a84d2 size 29159 diff --git a/Content/Editor/Terrain/Circle Brush Material.flax b/Content/Editor/Terrain/Circle Brush Material.flax index 6ed469392..6a7995a19 100644 --- a/Content/Editor/Terrain/Circle Brush Material.flax +++ b/Content/Editor/Terrain/Circle Brush Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3399c6639c78aed32767e27cdf3c8d5866a51bab8867f3ebceb1ffe5b886debe -size 27986 +oid sha256:44a95beefc351f6bfb385f0db16d4876b121763127e2a47dfb365a98a93ddd29 +size 28033 diff --git a/Content/Editor/Terrain/Highlight Terrain Material.flax b/Content/Editor/Terrain/Highlight Terrain Material.flax index 592a0c1f5..ae467fcd0 100644 --- a/Content/Editor/Terrain/Highlight Terrain Material.flax +++ b/Content/Editor/Terrain/Highlight Terrain Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:578169c0df3168d3984677e5682f29e9694ebc284229a217ae43dfd275734e58 -size 21367 +oid sha256:69adfef89795ec0561d9816a0c63ae4dc4316b802365efdb7e2b972ef82cb6e7 +size 21414 diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax index 5523b8376..0a9184d34 100644 --- a/Content/Editor/TexturePreviewMaterial.flax +++ b/Content/Editor/TexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91c76d393572fbcd535461d3815f1e0e475007792a7536f20dc5fa50ecb3c179 +oid sha256:bf0dffaeb28d26a77ead3cf5d01096b90e100c1a686dd969a636207b727d3b5c size 10570 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index aa5ddd243..84442f64b 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97c617375c8ca7ef511487b3d060e1f2a628c27388e23ad8f6fa862425d2a5b9 +oid sha256:dce8daa6593684c59cb573aca25433eaae66efe7c6d93faa2e40779d01ff9332 size 28549 diff --git a/Content/Engine/DefaultDeformableMaterial.flax b/Content/Engine/DefaultDeformableMaterial.flax index ba318b106..ed3edfcdc 100644 --- a/Content/Engine/DefaultDeformableMaterial.flax +++ b/Content/Engine/DefaultDeformableMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb66093810d808a26084f552b5a5da5daa0d3a57f7ea41d7e202e4b4d09129c4 +oid sha256:bda2374d296e4e80213b16f42e11a26198fd4bcd5db6cd04f3fa56d419ff3e68 size 18514 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index 6038d06be..4bcbda48c 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3207b95298f954ea187cce08b28db57ade1863f523626c10c3fd0c60ac8af42c +oid sha256:cd84e7d71f97eb06f6eb229ba2d29bf0145f3bd39a88754a93d6ed24922d1cd2 size 29992 diff --git a/Content/Engine/DefaultRadialMenu.flax b/Content/Engine/DefaultRadialMenu.flax index 582ef5cfe..060d9f7b4 100644 --- a/Content/Engine/DefaultRadialMenu.flax +++ b/Content/Engine/DefaultRadialMenu.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29db27b56a03e53a04b10239d722ae69369d80d6acd7acfa090af833f7a2c584 +oid sha256:fc132311f3f1f83525252aa3a10d3f95d9070ec38db5d81b9d4d595b1b9c5757 size 20340 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index 355158581..c1e1d5902 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cce61627c31b12d2d79abf59c485889044b4b9a803c0d2b7a988dc25906d6a35 -size 30264 +oid sha256:a2c217b7417f6f69eea1bf49ae645d697385a56e8f41b2ebba93d40d5a775133 +size 23658 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index 56a190cd4..6571220e9 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31eba5266d5b1354caea2d4d29528661a5d8b50fd38a3fb70c36fb83b2033c9e +oid sha256:2aa65e7aaeb6ee9747d459b91912c1679d2629727dbb95bb4ce31aca8f3d084b size 28168 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index 9a4d091f2..dbde3990a 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:031971460da7490fdb2645a580003a9fbb99a403f10759285ccc36fc8b6a1c88 +oid sha256:4222013b2d5540977ae3ec82f2631b604055467f30aec6f0cab332e13fe485b8 size 29366 diff --git a/Content/Shaders/Editor/Grid.flax b/Content/Shaders/Editor/Grid.flax index 1c87a1332..440d37512 100644 --- a/Content/Shaders/Editor/Grid.flax +++ b/Content/Shaders/Editor/Grid.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce516159bb427e1c2cbd48f86be17b2b3c065d2ecd6943aace05451853d6b3e1 -size 4626 +oid sha256:9aa0cb389296afe4ee474395f188e61f8a1e428aac1f09fc7d54825b1d6e58df +size 4744 diff --git a/README.md b/README.md index 5e1374e02..c4d6e7298 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Follow the instructions below to compile and run the engine from source. * Install Visual Studio 2022 or newer * Install Windows 8.1 SDK or newer (via Visual Studio Installer) * Install Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer) -* Install .NET 8 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) +* Install .NET 8 or 9 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Install Git with LFS * Clone repo (with LFS) * Run **GenerateProjectFiles.bat** @@ -44,8 +44,9 @@ Follow the instructions below to compile and run the engine from source. ## Linux * Install Visual Studio Code -* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) +* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Ubuntu: `sudo apt install dotnet-sdk-8.0` + * Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host` * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Ubuntu: `sudo apt install vulkan-sdk` * Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers` @@ -67,12 +68,12 @@ Follow the instructions below to compile and run the engine from source. ## Mac * Install XCode -* Install .NET 8 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) +* Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Clone repo (with LFS) * Run `GenerateProjectFiles.command` * Open workspace with XCode or Visual Studio Code -* Build and run (configuration `Editor.Mac.Development`) +* Build and run (configuration `Editor.Mac.Development`) #### Troubleshooting diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 18c5b6d7b..77e34dc70 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -750,7 +750,8 @@ namespace FlaxEditor.Content // Draw short name Render2D.PushClip(ref textRect); - Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); + var scale = 0.95f * view.ViewScale; + Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, scale); Render2D.PopClip(); if (IsBeingCut) diff --git a/Source/Editor/Content/Proxy/CSharpProxy.cs b/Source/Editor/Content/Proxy/CSharpProxy.cs index 8abdf8b30..f0a7997c7 100644 --- a/Source/Editor/Content/Proxy/CSharpProxy.cs +++ b/Source/Editor/Content/Proxy/CSharpProxy.cs @@ -106,6 +106,23 @@ namespace FlaxEditor.Content } } + /// + /// Context proxy object for C# GamePlugin files. + /// + /// + [ContentContextMenu("New/C#/C# GamePlugin")] + public class CSharpGamePluginProxy : CSharpProxy + { + /// + public override string Name => "C# GamePlugin"; + + /// + protected override void GetTemplatePath(out string path) + { + path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/GamePluginTemplate.cs"); + } + } + /// /// Context proxy object for empty C# files. /// diff --git a/Source/Editor/Content/Proxy/JsonAssetProxy.cs b/Source/Editor/Content/Proxy/JsonAssetProxy.cs index 27d0e2347..dfdec8330 100644 --- a/Source/Editor/Content/Proxy/JsonAssetProxy.cs +++ b/Source/Editor/Content/Proxy/JsonAssetProxy.cs @@ -1,9 +1,11 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.Content.Create; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.Scripting; using FlaxEditor.Windows; using FlaxEditor.Windows.Assets; using FlaxEngine; @@ -84,18 +86,67 @@ namespace FlaxEditor.Content if (_element != null) { - // Define the rule for the types that can be used to create a json data asset - _element.CustomControl.CheckValid += type => - type.Type != null && - type.IsClass && - type.Type.IsVisible && - !type.IsAbstract && - !type.IsGenericType && - type.Type.GetConstructor(Type.EmptyTypes) != null && - !typeof(FlaxEngine.GUI.Control).IsAssignableFrom(type.Type) && - !typeof(FlaxEngine.Object).IsAssignableFrom(type.Type); + _element.CustomControl.CheckValid += OnCheckValidJsonAssetType; } } + + private static Type[] BlacklistedClasses = + [ + typeof(System.Attribute), + typeof(FlaxEngine.Object), + typeof(FlaxEngine.GUI.Control), + ]; + + private static Type[] BlacklistedStructs = + [ + typeof(Float2), + typeof(Float3), + typeof(Float4), + typeof(Double2), + typeof(Double3), + typeof(Double4), + typeof(Vector2), + typeof(Vector3), + typeof(Vector4), + typeof(Half2), + typeof(Half3), + typeof(Half4), + typeof(Int2), + typeof(Int3), + typeof(Int4), + typeof(Transform), + typeof(Quaternion), + typeof(BoundingBox), + typeof(BoundingSphere), + typeof(BoundingFrustum), + typeof(Ray), + typeof(Plane), + typeof(Matrix), + typeof(Color), + typeof(Color32), + typeof(FloatR11G11B10), + typeof(FloatR10G10B10A2), + typeof(FlaxEngine.Half), + ]; + + private static bool OnCheckValidJsonAssetType(ScriptType type) + { + // Define the rule for the types that can be used to create a json data asset + var mType = type.Type; + if (mType == null || + type.IsAbstract || + type.IsStatic || + type.IsGenericType || + !mType.IsVisible) + return false; + if (type.IsClass) + return mType.GetConstructor(Type.EmptyTypes) != null && BlacklistedClasses.FirstOrDefault(x => x.IsAssignableFrom(mType)) == null; + if (type.IsStructure) + return !type.IsPrimitive && + !type.IsVoid && + !BlacklistedStructs.Contains(mType); + return false; + } } } @@ -175,7 +226,7 @@ namespace FlaxEditor.Content { _thumbnail = SpriteHandle.Invalid; } - + /// /// Constructor with overriden thumbnail. /// @@ -196,7 +247,7 @@ namespace FlaxEditor.Content { Editor.SaveJsonAsset(outputPath, new T()); } - + /// public override AssetItem ConstructItem(string path, string typeName, ref Guid id) { diff --git a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs index b3d7ffa49..524bebfc3 100644 --- a/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs +++ b/Source/Editor/Content/Thumbnails/ThumbnailsModule.cs @@ -496,7 +496,7 @@ namespace FlaxEditor.Content.Thumbnails // Prepare requests bool isAnyReady = false; int checks = Mathf.Min(10, _requests.Count); - for (int i = 0; i < checks; i++) + for (int i = 0; i < checks && i < _requests.Count; i++) { var request = _requests[i]; try diff --git a/Source/Editor/Cooker/GameCooker.cpp b/Source/Editor/Cooker/GameCooker.cpp index 17392adee..36f2da11c 100644 --- a/Source/Editor/Cooker/GameCooker.cpp +++ b/Source/Editor/Cooker/GameCooker.cpp @@ -671,11 +671,14 @@ bool GameCookerImpl::Build() MCore::Thread::Attach(); // Build Started - CallEvent(GameCooker::EventType::BuildStarted); - data.Tools->OnBuildStarted(data); - for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) - Steps[stepIndex]->OnBuildStarted(data); - data.InitProgress(Steps.Count()); + if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook)) + { + CallEvent(GameCooker::EventType::BuildStarted); + data.Tools->OnBuildStarted(data); + for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) + Steps[stepIndex]->OnBuildStarted(data); + data.InitProgress(Steps.Count()); + } // Execute all steps in a sequence bool failed = false; @@ -741,10 +744,13 @@ bool GameCookerImpl::Build() } IsRunning = false; CancelFlag = 0; - for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) - Steps[stepIndex]->OnBuildEnded(data, failed); - data.Tools->OnBuildEnded(data, failed); - CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone); + if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook)) + { + for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) + Steps[stepIndex]->OnBuildEnded(data, failed); + data.Tools->OnBuildEnded(data, failed); + CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone); + } Delete(Data); Data = nullptr; diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index eb5469b47..9e4f18b2c 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -364,6 +364,33 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) } #endif const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release; + + if (platformSettings->BuildAAB) + { + // .aab + { + CreateProcessSettings procSettings; + procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT(":app:bundle") : TEXT(":app:bundleDebug")); + procSettings.WorkingDirectory = data.OriginalOutputPath; + const int32 result = Platform::CreateProcess(procSettings); + if (result != 0) + { + data.Error(String::Format(TEXT("Failed to build Gradle project into .aab package (result code: {0}). See log for more info."), result)); + return true; + } + } + // Copy result package + const String aab = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/bundle/release/app-release.aab") : TEXT("app/build/outputs/bundle/debug/app-debug.aab")); + const String outputAab = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".aab"); + if (FileSystem::CopyFile(outputAab, aab)) + { + LOG(Error, "Failed to copy .aab package from {0} to {1}", aab, outputAab); + return true; + } + LOG(Info, "Output Android AAB application package: {0} (size: {1} MB)", outputAab, FileSystem::GetFileSize(outputAab) / 1024 / 1024); + } + + // .apk { CreateProcessSettings procSettings; procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug")); @@ -371,20 +398,20 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) const int32 result = Platform::CreateProcess(procSettings); if (result != 0) { - data.Error(String::Format(TEXT("Failed to build Gradle project into package (result code: {0}). See log for more info."), result)); + data.Error(String::Format(TEXT("Failed to build Gradle project into .apk package (result code: {0}). See log for more info."), result)); return true; } } - // Copy result package const String apk = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/apk/release/app-release-unsigned.apk") : TEXT("app/build/outputs/apk/debug/app-debug.apk")); const String outputApk = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".apk"); if (FileSystem::CopyFile(outputApk, apk)) { - LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk); + LOG(Error, "Failed to copy .apk package from {0} to {1}", apk, outputApk); return true; } - LOG(Info, "Output Android application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024); + LOG(Info, "Output Android APK application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024); + return false; } diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp index 6b1a6829e..489321d99 100644 --- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp +++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp @@ -214,6 +214,33 @@ bool DeployDataStep::Perform(CookingData& data) FileSystem::NormalizePath(srcDotnet); LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), srcDotnet); + // Get major Version + Array pathParts; + srcDotnet.Split('/', pathParts); + String version; + for (int i = 0; i < pathParts.Count(); i++) + { + if (pathParts[i] == TEXT("runtimes")) + { + Array versionParts; + pathParts[i - 1].Split('.', versionParts); + if (!versionParts.IsEmpty()) + { + const String majorVersion = versionParts[0].TrimTrailing(); + int32 versionNum; + StringUtils::Parse(*majorVersion, majorVersion.Length(), &versionNum); + if (Math::IsInRange(versionNum, GAME_BUILD_DOTNET_RUNTIME_MIN_VER, GAME_BUILD_DOTNET_RUNTIME_MAX_VER)) // Check for major part + version = majorVersion; + } + } + } + + if (version.IsEmpty()) + { + data.Error(TEXT("Failed to find supported .NET version for the current host platform.")); + return true; + } + // Deploy runtime files const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll"); const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms")); @@ -226,14 +253,14 @@ bool DeployDataStep::Perform(CookingData& data) { // AOT runtime files inside Engine Platform folder packFolder /= TEXT("Dotnet"); - dstDotnetLibs /= TEXT("lib/net8.0"); - srcDotnetLibs = packFolder / TEXT("lib/net8.0"); + dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version); + srcDotnetLibs = packFolder / String::Format(TEXT("lib/net{}.0"), version); } else { // Runtime files inside Dotnet SDK folder but placed for AOT - dstDotnetLibs /= TEXT("lib/net8.0"); - srcDotnetLibs /= TEXT("../lib/net8.0"); + dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version); + srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version); } } else @@ -241,14 +268,14 @@ bool DeployDataStep::Perform(CookingData& data) if (srcDotnetFromEngine) { // Runtime files inside Engine Platform folder - dstDotnetLibs /= TEXT("lib/net8.0"); - srcDotnetLibs /= TEXT("lib/net8.0"); + dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version); + srcDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version); } else { // Runtime files inside Dotnet SDK folder dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App"); - srcDotnetLibs /= TEXT("../lib/net8.0"); + srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version); } } LOG(Info, "Copying .NET files from {} to {}", packFolder, dstDotnet); @@ -273,6 +300,7 @@ bool DeployDataStep::Perform(CookingData& data) DEPLOY_NATIVE_FILE("libmonosgen-2.0.so"); DEPLOY_NATIVE_FILE("libSystem.IO.Compression.Native.so"); DEPLOY_NATIVE_FILE("libSystem.Native.so"); + DEPLOY_NATIVE_FILE("libSystem.Globalization.Native.so"); DEPLOY_NATIVE_FILE("libSystem.Security.Cryptography.Native.Android.so"); break; case BuildPlatform::iOSARM64: diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 8c3810c3c..79c00b38a 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -883,7 +883,7 @@ namespace FlaxEditor.CustomEditors /// protected virtual void ClearToken() { - ParentEditor.ClearToken(); + ParentEditor?.ClearToken(); } } } diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 27e2849bd..d1cdd8780 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -9,6 +9,7 @@ using FlaxEditor.CustomEditors.Elements; using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Tree; +using FlaxEditor.Modules; using FlaxEditor.Scripting; using FlaxEditor.Windows; using FlaxEditor.Windows.Assets; @@ -67,12 +68,16 @@ namespace FlaxEditor.CustomEditors.Dedicated // Use default prefab instance as a reference for the editor Values.SetReferenceValue(prefabInstance); - if (Presenter == Editor.Instance.Windows.PropertiesWin.Presenter) + // Display prefab UI (when displaying object inside Prefab Window then display only nested prefabs) + var prefabId = prefab.ID; + Editor.GetPrefabNestedObject(ref prefabId, ref prefabObjectId, out var nestedPrefabId, out var nestedPrefabObjectId); + var nestedPrefab = FlaxEngine.Content.Load(nestedPrefabId); + var panel = layout.CustomContainer(); + panel.CustomControl.Height = 20.0f; + panel.CustomControl.SlotsVertically = 1; + if (Presenter == Editor.Instance.Windows.PropertiesWin.Presenter || nestedPrefab) { - // Add some UI - var panel = layout.CustomContainer(); - panel.CustomControl.Height = 20.0f; - panel.CustomControl.SlotsVertically = 1; + var targetPrefab = nestedPrefab ?? prefab; panel.CustomControl.SlotsHorizontally = 3; // Selecting actor prefab asset @@ -80,22 +85,21 @@ namespace FlaxEditor.CustomEditors.Dedicated selectPrefab.Button.Clicked += () => { Editor.Instance.Windows.ContentWin.ClearItemsSearch(); - Editor.Instance.Windows.ContentWin.Select(prefab); + Editor.Instance.Windows.ContentWin.Select(targetPrefab); }; // Edit selected prefab asset var editPrefab = panel.Button("Edit Prefab"); - editPrefab.Button.Clicked += () => - { - Editor.Instance.Windows.ContentWin.ClearItemsSearch(); - Editor.Instance.Windows.ContentWin.Select(prefab); - Editor.Instance.Windows.ContentWin.Open(Editor.Instance.Windows.ContentWin.View.Selection[0]); - }; - - // Viewing changes applied to this actor - var viewChanges = panel.Button("View Changes"); - viewChanges.Button.Clicked += () => ViewChanges(viewChanges.Button, new Float2(0.0f, 20.0f)); + editPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Open(Editor.Instance.ContentDatabase.FindAsset(targetPrefab.ID)); } + else + { + panel.CustomControl.SlotsHorizontally = 1; + } + + // Viewing changes applied to this actor + var viewChanges = panel.Button("View Changes"); + viewChanges.Button.Clicked += () => ViewChanges(viewChanges.Button, new Float2(0.0f, 20.0f)); // Link event to update editor on prefab apply _linkedPrefabId = prefab.ID; diff --git a/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs b/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs index 5d06b4a94..f48e1b9c1 100644 --- a/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.Surface; using FlaxEngine; @@ -35,6 +36,8 @@ namespace FlaxEditor.CustomEditors.Dedicated (instance, parameter, tag) => ((AnimatedModel)instance).GetParameterValue(parameter.Identifier), (instance, value, parameter, tag) => ((AnimatedModel)instance).SetParameterValue(parameter.Identifier, value), Values); + if (!parameters.Any()) + group.Label("No parameters", TextAlignment.Center); _parametersAdded = true; } } diff --git a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs index 81d826a04..a39297572 100644 --- a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs @@ -15,13 +15,23 @@ namespace FlaxEditor.CustomEditors.Dedicated private int _firstTimeShow; private BezierCurveEditor _curve; private Splitter _splitter; + private string _heightCachedPath; /// public override void Initialize(LayoutElementsContainer layout) { var item = layout.CustomContainer>(); _curve = item.CustomControl; - _curve.Height = 120.0f; + var height = 120.0f; + var presenter = Presenter; + if (presenter != null && (presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) + { + // Try to restore curve height + _heightCachedPath = layout.GetLayoutCachePath("Height"); + if (Editor.Instance.ProjectCache.TryGetCustomData(_heightCachedPath, out float cachedHeight) && cachedHeight > 10.0f) + height = cachedHeight; + } + _curve.Height = height; _curve.Edited += OnCurveEdited; _firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing) _splitter = new Splitter @@ -45,7 +55,11 @@ namespace FlaxEditor.CustomEditors.Dedicated private void OnSplitterMoved(Float2 location) { - _curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f); + _curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f); + + // Cache curve height + if (_heightCachedPath != null) + Editor.Instance.ProjectCache.SetCustomData(_heightCachedPath, _curve.Height); } /// @@ -133,13 +147,23 @@ namespace FlaxEditor.CustomEditors.Dedicated private int _firstTimeShow; private LinearCurveEditor _curve; private Splitter _splitter; + private string _heightCachedPath; /// public override void Initialize(LayoutElementsContainer layout) { var item = layout.CustomContainer>(); _curve = item.CustomControl; - _curve.Height = 120.0f; + var height = 120.0f; + var presenter = Presenter; + if (presenter != null && (presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) + { + // Try to restore curve height + _heightCachedPath = layout.GetLayoutCachePath("Height"); + if (Editor.Instance.ProjectCache.TryGetCustomData(_heightCachedPath, out float cachedHeight) && cachedHeight > 10.0f) + height = cachedHeight; + } + _curve.Height = height; _curve.Edited += OnCurveEdited; _firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing) _splitter = new Splitter @@ -164,6 +188,10 @@ namespace FlaxEditor.CustomEditors.Dedicated private void OnSplitterMoved(Float2 location) { _curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f); + + // Cache curve height + if (_heightCachedPath != null) + Editor.Instance.ProjectCache.SetCustomData(_heightCachedPath, _curve.Height); } /// diff --git a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs index fbb817768..3df5cb72e 100644 --- a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs @@ -53,6 +53,9 @@ public class ModelPrefabEditor : GenericEditor } } + // Creates the import path UI + Utilities.Utils.CreateImportPathUI(layout, modelPrefab.ImportPath, false); + var button = layout.Button("Reimport", "Reimports the source asset as prefab."); _reimportButton = button.Button; _reimportButton.Clicked += OnReimport; diff --git a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs index c207f8949..980ad5fa4 100644 --- a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs @@ -117,6 +117,9 @@ namespace FlaxEditor.CustomEditors.Dedicated var data = SurfaceUtils.InitGraphParameters(parametersGroup); SurfaceUtils.DisplayGraphParameters(group, data, ParameterGet, ParameterSet, Values, ParameterDefaultValue); } + + if (!parameters.Any()) + groups.Label("No parameters", TextAlignment.Center); } /// diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 83fa1f129..e9c405207 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using FlaxEditor.Content; using FlaxEditor.GUI; +using FlaxEditor.GUI.Drag; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; @@ -122,7 +123,9 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); - if (!HasDifferentValues) + var differentValues = HasDifferentValues; + Picker.DifferentValues = differentValues; + if (!differentValues) { _isRefreshing = true; var value = Values[0]; @@ -156,6 +159,17 @@ namespace FlaxEditor.CustomEditors.Editors private Rectangle DropdownRect => new Rectangle(Width - DropdownIconSize - DropdownIconMargin, DropdownIconMargin, DropdownIconSize, DropdownIconSize); public Action ShowPicker; + public Action OnAssetDropped; + + private DragItems _dragItems; + private DragHandlers _dragHandlers; + private bool _hasValidDragOver; + private Func _validate; + + public void SetValidationMethod(Func validate) + { + _validate = validate; + } public override void Draw() { @@ -164,6 +178,14 @@ namespace FlaxEditor.CustomEditors.Editors var style = FlaxEngine.GUI.Style.Current; var dropdownRect = DropdownRect; Render2D.DrawSprite(style.ArrowDown, dropdownRect, Enabled ? (DropdownRect.Contains(PointFromWindow(RootWindow.MousePosition)) ? style.BorderSelected : style.Foreground) : style.ForegroundDisabled); + + // Check if drag is over + if (IsDragOver && _hasValidDragOver) + { + var bounds = new Rectangle(Float2.Zero, Size); + Render2D.FillRectangle(bounds, style.Selection); + Render2D.DrawRectangle(bounds, style.SelectionBorder); + } } public override bool OnMouseDown(Float2 location, MouseButton button) @@ -207,6 +229,68 @@ namespace FlaxEditor.CustomEditors.Editors return result; } } + + private DragDropEffect DragEffect => _hasValidDragOver ? DragDropEffect.Move : DragDropEffect.None; + + /// + public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) + { + base.OnDragEnter(ref location, data); + + // Ensure to have valid drag helpers (uses lazy init) + if (_dragItems == null) + _dragItems = new DragItems(ValidateDragAsset); + if (_dragHandlers == null) + { + _dragHandlers = new DragHandlers + { + _dragItems, + }; + } + + _hasValidDragOver = _dragHandlers.OnDragEnter(data) != DragDropEffect.None; + + + return DragEffect; + } + + private bool ValidateDragAsset(ContentItem contentItem) + { + // Load or get asset + return _validate?.Invoke(contentItem) ?? false; + } + + /// + public override DragDropEffect OnDragMove(ref Float2 location, DragData data) + { + base.OnDragMove(ref location, data); + + return DragEffect; + } + + /// + public override void OnDragLeave() + { + _hasValidDragOver = false; + _dragHandlers.OnDragLeave(); + + base.OnDragLeave(); + } + + /// + public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) + { + var result = DragEffect; + + base.OnDragDrop(ref location, data); + + if (_dragItems.HasValidDrag) + { + OnAssetDropped(_dragItems.Objects[0]); + } + + return result; + } } private TextBoxWithPicker _textBox; @@ -221,13 +305,21 @@ namespace FlaxEditor.CustomEditors.Editors { if (HasDifferentTypes) return; + + _validator = new AssetPickerValidator(ScriptType.Null); _textBox = layout.Custom().CustomControl; _textBox.ShowPicker = OnShowPicker; + _textBox.OnAssetDropped = OnItemDropped; _textBox.EditEnd += OnEditEnd; - _validator = new AssetPickerValidator(ScriptType.Null); + _textBox.SetValidationMethod(_validator.IsValid); AssetRefEditor.ApplyAssetReferenceAttribute(Values, out _, _validator); } + private void OnItemDropped(ContentItem item) + { + SetPickerPath(item); + } + private void OnShowPicker() { if (_validator.AssetType != ScriptType.Null) @@ -285,12 +377,9 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); - if (!HasDifferentValues) - { - _isRefreshing = true; - _textBox.Text = GetPath(); - _isRefreshing = false; - } + _isRefreshing = true; + _textBox.Text = HasDifferentValues ? "Multiple Values" : GetPath(); + _isRefreshing = false; } /// diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index 386d8d518..1dc624525 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -9,6 +9,8 @@ using FlaxEditor.GUI.Drag; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.GUI; using FlaxEditor.Scripting; +using FlaxEditor.Windows; +using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; @@ -40,6 +42,11 @@ namespace FlaxEditor.CustomEditors.Editors private DragScripts _dragScripts; private DragHandlers _dragHandlers; + /// + /// The presenter using this control. + /// + public IPresenterOwner PresenterContext; + /// /// Gets or sets the allowed objects type (given type and all sub classes). Must be type of any subclass. /// @@ -129,6 +136,11 @@ namespace FlaxEditor.CustomEditors.Editors /// public Func CheckValid; + /// + /// Utility flag used to indicate that there are different values assigned to this reference editor and user should be informed about it. + /// + public bool DifferentValues; + /// /// Initializes a new instance of the class. /// @@ -154,7 +166,7 @@ namespace FlaxEditor.CustomEditors.Editors Value = actor; RootWindow.Focus(); Focus(); - }); + }, PresenterContext); } else { @@ -163,7 +175,7 @@ namespace FlaxEditor.CustomEditors.Editors Value = script; RootWindow.Focus(); Focus(); - }); + }, PresenterContext); } } @@ -197,7 +209,14 @@ namespace FlaxEditor.CustomEditors.Editors Render2D.DrawRectangle(frameRect, isEnabled && (IsMouseOver || IsNavFocused) ? style.BorderHighlighted : style.BorderNormal); // Check if has item selected - if (isSelected) + if (DifferentValues) + { + // Draw info + Render2D.PushClip(nameRect); + Render2D.DrawText(style.FontMedium, Type != null ? $"Multiple Values ({Utilities.Utils.GetPropertyNameUI(Type.ToString())})" : "-", nameRect, isEnabled ? style.ForegroundGrey : style.ForegroundGrey.AlphaMultiplied(0.75f), TextAlignment.Near, TextAlignment.Center); + Render2D.PopClip(); + } + else if (isSelected) { // Draw name Render2D.PushClip(nameRect); @@ -415,13 +434,13 @@ namespace FlaxEditor.CustomEditors.Editors // Ensure to have valid drag helpers (uses lazy init) if (_dragActors == null) - _dragActors = new DragActors(x => IsValid(x.Actor)); + _dragActors = new DragActors(ValidateDragActor); if (_dragActorsWithScript == null) _dragActorsWithScript = new DragActors(ValidateDragActorWithScript); if (_dragAssets == null) _dragAssets = new DragAssets(ValidateDragAsset); if (_dragScripts == null) - _dragScripts = new DragScripts(IsValid); + _dragScripts = new DragScripts(ValidateDragScript); if (_dragHandlers == null) { _dragHandlers = new DragHandlers @@ -446,6 +465,43 @@ namespace FlaxEditor.CustomEditors.Editors return DragEffect; } + private bool ValidateDragActor(ActorNode a) + { + if (!IsValid(a.Actor)) + return false; + + if (PresenterContext is PrefabWindow prefabWindow) + { + if (prefabWindow.Tree == a.TreeNode.ParentTree) + return true; + } + else if (PresenterContext is PropertiesWindow || PresenterContext == null) + { + if (a.ParentScene != null) + return true; + } + return false; + } + + private bool ValidateDragScript(Script script) + { + if (!IsValid(script)) + return false; + + if (PresenterContext is PrefabWindow prefabWindow) + { + var actorNode = prefabWindow.Graph.Root.Find(script.Actor); + if (actorNode != null) + return true; + } + else if (PresenterContext is PropertiesWindow || PresenterContext == null) + { + if (script.Actor.HasScene) + return true; + } + return false; + } + private bool ValidateDragAsset(AssetItem assetItem) { // Check if can accept assets @@ -464,7 +520,18 @@ namespace FlaxEditor.CustomEditors.Editors private bool ValidateDragActorWithScript(ActorNode node) { - return node.Actor.Scripts.Any(IsValid); + bool isCorrectContext = false; + if (PresenterContext is PrefabWindow prefabWindow) + { + if (prefabWindow.Tree == node.TreeNode.ParentTree) + isCorrectContext = true; + } + else if (PresenterContext is PropertiesWindow || PresenterContext == null) + { + if (node.ParentScene != null) + isCorrectContext = true; + } + return node.Actor.Scripts.Any(IsValid) && isCorrectContext; } /// @@ -536,6 +603,7 @@ namespace FlaxEditor.CustomEditors.Editors if (!HasDifferentTypes) { _element = layout.Custom(); + _element.CustomControl.PresenterContext = Presenter.Owner; _element.CustomControl.Type = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]); _element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value); } @@ -546,7 +614,9 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); - if (!HasDifferentValues) + var differentValues = HasDifferentValues; + _element.CustomControl.DifferentValues = differentValues; + if (!differentValues) { _element.CustomControl.Value = Values[0] as Object; } diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 9e01e8323..38e380550 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -228,7 +228,6 @@ namespace FlaxEditor.CustomEditors.Editors // Handle Sliding if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded)) { - // TODO: handle linked values Float3 average = Float3.Zero; for (int i = 0; i < Values.Count; i++) { @@ -251,12 +250,24 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < Values.Count; i++) { var v = Values[i]; - if (v is Vector3 asVector3) - v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); - else if (v is Float3 asFloat3) - v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); - else if (v is Double3 asDouble3) - v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); + if (LinkValues) + { + if (v is Vector3 asVector3) + v = asVector3 + new Vector3(newValue.X, newValue.Y, newValue.Z); + else if (v is Float3 asFloat3) + v = asFloat3 + new Float3(newValue.X, newValue.Y, newValue.Z); + else if (v is Double3 asDouble3) + v = asDouble3 + new Double3(newValue.X, newValue.Y, newValue.Z); + } + else + { + if (v is Vector3 asVector3) + v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); + else if (v is Float3 asFloat3) + v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); + else if (v is Double3 asDouble3) + v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0); + } newObjects[i] = v; } @@ -267,16 +278,27 @@ namespace FlaxEditor.CustomEditors.Editors } else { - // TODO: handle linked values for (int i = 0; i < Values.Count; i++) { object v = Values[i]; - if (v is Vector3 asVector3) - v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z); - else if (v is Float3 asFloat3) - v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z); - else if (v is Double3 asDouble3) - v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z); + if (LinkValues) + { + if (v is Vector3 asVector3) + v = asVector3 + new Vector3(xValue, yValue, zValue); + else if (v is Float3 asFloat3) + v = asFloat3 + new Float3(xValue, yValue, zValue); + else if (v is Double3 asDouble3) + v = asDouble3 + new Double3(xValue, yValue, zValue); + } + else + { + if (v is Vector3 asVector3) + v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z); + else if (v is Float3 asFloat3) + v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z); + else if (v is Double3 asDouble3) + v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z); + } newObjects[i] = v; } diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 5b0e2f742..9fa090902 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -96,6 +96,20 @@ namespace FlaxEditor.CustomEditors menu.Show(groupPanel, location); } + internal string GetLayoutCachePath(string name) + { + // Build group identifier (made of path from group titles) + var expandPath = name; + var container = this; + while (container != null && !(container is CustomEditorPresenter)) + { + if (container.ContainerControl is DropPanel dropPanel) + expandPath = dropPanel.HeaderText + "/" + expandPath; + container = container._parent; + } + return expandPath; + } + /// /// Adds new group element. /// @@ -112,14 +126,7 @@ namespace FlaxEditor.CustomEditors if (presenter != null && (presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) { // Build group identifier (made of path from group titles) - var expandPath = title; - var container = this; - while (container != null && !(container is CustomEditorPresenter)) - { - if (container.ContainerControl is DropPanel dropPanel) - expandPath = dropPanel.HeaderText + "/" + expandPath; - container = container._parent; - } + var expandPath = GetLayoutCachePath(title); // Caching/restoring expanded groups (non-root groups cache expanded state so invert boolean expression) if (Editor.Instance.ProjectCache.IsGroupToggled(expandPath) ^ isSubGroup) diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs index 904d5bc09..3c360b4b3 100644 --- a/Source/Editor/CustomEditors/Values/ValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs @@ -250,7 +250,7 @@ namespace FlaxEditor.CustomEditors if (objA == null && objB is string objBStr && objBStr.Length == 0) return true; - return Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals(objA, objB); + return FlaxEngine.Json.JsonSerializer.ValueEquals(objA, objB); } /// diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index 8e4090c40..3d77445b9 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -550,7 +550,7 @@ int32 Editor::LoadProduct() } if (!FileSystem::FileExists(files[0])) { - Platform::Fatal(TEXT("Cannot opoen selected project file because it doesn't exist.")); + Platform::Fatal(TEXT("Cannot open selected project file because it doesn't exist.")); return -1; } projectPath = StringUtils::GetDirectoryName(files[0]); diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 795f89d55..3a11ed449 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1686,9 +1686,6 @@ namespace FlaxEditor [return: MarshalAs(UnmanagedType.U1)] internal static partial bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot); - [LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetPrefabNestedObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] - internal static partial void Internal_GetPrefabNestedObject(IntPtr prefabId, IntPtr prefabObjectId, IntPtr outPrefabId, IntPtr outPrefabObjectId); - [LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] internal static partial float Internal_GetAnimationTime(IntPtr animatedModel); diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 7aca96c4e..92941be5d 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -48,6 +48,11 @@ namespace FlaxEditor.GUI /// public bool CanEdit = true; + /// + /// Utility flag used to indicate that there are different values assigned to this reference editor and user should be informed about it. + /// + public bool DifferentValues; + /// /// Initializes a new instance of the class. /// @@ -121,7 +126,13 @@ namespace FlaxEditor.GUI if (CanEdit) Render2D.DrawSprite(style.ArrowDown, button1Rect, button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey); - if (Validator.SelectedItem != null) + if (DifferentValues) + { + // No element selected + Render2D.FillRectangle(iconRect, style.BackgroundNormal); + Render2D.DrawText(style.FontMedium, "Multiple\nValues", iconRect, style.Foreground, TextAlignment.Center, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, Height / DefaultIconSize); + } + else if (Validator.SelectedItem != null) { // Draw item preview Validator.SelectedItem.DrawThumbnail(ref iconRect); diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index 25954abfa..510e429ae 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -30,8 +30,10 @@ namespace FlaxEditor.GUI internal bool _isMovingTangent; internal bool _movedView; internal bool _movedKeyframes; + internal bool _toggledSelection; private TangentPoint _movingTangent; private Float2 _movingSelectionStart; + private Float2 _movingSelectionStartPosLock; private Float2[] _movingSelectionOffsets; private Float2 _cmShowPos; @@ -56,12 +58,11 @@ namespace FlaxEditor.GUI internal void UpdateSelection(ref Rectangle selectionRect) { // Find controls to select - for (int i = 0; i < Children.Count; i++) + var children = _children; + for (int i = 0; i < children.Count; i++) { - if (Children[i] is KeyframePoint p) - { + if (children[i] is KeyframePoint p) p.IsSelected = p.Bounds.Intersects(ref selectionRect); - } } _editor.UpdateTangents(); } @@ -72,6 +73,7 @@ namespace FlaxEditor.GUI _isMovingSelection = true; _movedKeyframes = false; var viewRect = _editor._mainPanel.GetClientArea(); + _movingSelectionStartPosLock = location; _movingSelectionStart = PointToKeyframes(location, ref viewRect); if (_movingSelectionOffsets == null || _movingSelectionOffsets.Length != _editor._points.Count) _movingSelectionOffsets = new Float2[_editor._points.Count]; @@ -82,10 +84,17 @@ namespace FlaxEditor.GUI internal void OnMove(Float2 location) { + // Skip updating keyframes until move actual starts to be meaningful + if (Float2.Distance(ref _movingSelectionStartPosLock, ref location) < 1.5f) + return; + _movingSelectionStartPosLock = Float2.Minimum; + var viewRect = _editor._mainPanel.GetClientArea(); var locationKeyframes = PointToKeyframes(location, ref viewRect); var accessor = _editor.Accessor; var components = accessor.GetCurveComponents(); + var snapEnabled = Root.GetKey(KeyboardKeys.Control); + var snapGrid = snapEnabled ? _editor.GetGridSnap() : Float2.One; for (var i = 0; i < _editor._points.Count; i++) { var p = _editor._points[i]; @@ -122,7 +131,20 @@ namespace FlaxEditor.GUI if (isFirstSelected) { time = locationKeyframes.X + offset.X; + } + if (snapEnabled) + { + // Snap to the grid + var key = new Float2(time, value); + key = Float2.SnapToGrid(key, snapGrid); + time = key.X; + value = key.Y; + } + + // Clamp and snap time to the valid range + if (isFirstSelected) + { if (_editor.FPS.HasValue) { float fps = _editor.FPS.Value; @@ -131,8 +153,6 @@ namespace FlaxEditor.GUI time = Mathf.Clamp(time, minTime, maxTime); } - // TODO: snapping keyframes to grid when moving - _editor.SetKeyframeInternal(p.Index, time, value, p.Component); } _editor.UpdateKeyframes(); @@ -234,7 +254,11 @@ namespace FlaxEditor.GUI var k = _editor.GetKeyframe(_movingTangent.Index); var kv = _editor.GetKeyframeValue(k); var value = _editor.Accessor.GetCurveValue(ref kv, _movingTangent.Component); - _movingTangent.TangentValue = PointToKeyframes(location, ref viewRect).Y - value; + var tangent = PointToKeyframes(location, ref viewRect).Y - value; + if (Root.GetKey(KeyboardKeys.Control)) + tangent = Float2.SnapToGrid(new Float2(0, tangent), _editor.GetGridSnap()).Y; // Snap tangent over Y axis + tangent = tangent * _editor.ViewScale.X * 2; + _movingTangent.TangentValue = tangent; _editor.UpdateTangents(); Cursor = CursorType.SizeNS; _movedKeyframes = true; @@ -283,6 +307,7 @@ namespace FlaxEditor.GUI } // Cache data + _toggledSelection = false; _isMovingSelection = false; _isMovingTangent = false; _mousePos = location; @@ -305,13 +330,7 @@ namespace FlaxEditor.GUI { if (_leftMouseDown) { - if (Root.GetKey(KeyboardKeys.Control)) - { - // Toggle selection - keyframe.IsSelected = !keyframe.IsSelected; - _editor.UpdateTangents(); - } - else if (Root.GetKey(KeyboardKeys.Shift)) + if (Root.GetKey(KeyboardKeys.Shift)) { // Select range keyframe.IsSelected = true; @@ -335,10 +354,14 @@ namespace FlaxEditor.GUI else if (!keyframe.IsSelected) { // Select node - if (_editor.KeyframesEditorContext != null) - _editor.KeyframesEditorContext.OnKeyframesDeselect(_editor); - else - _editor.ClearSelection(); + if (!Root.GetKey(KeyboardKeys.Control)) + { + if (_editor.KeyframesEditorContext != null) + _editor.KeyframesEditorContext.OnKeyframesDeselect(_editor); + else + _editor.ClearSelection(); + } + _toggledSelection = true; keyframe.IsSelected = true; _editor.UpdateTangents(); } @@ -429,6 +452,12 @@ namespace FlaxEditor.GUI else OnMoveEnd(location); } + // Toggle selection + else if (!_toggledSelection && Root.GetKey(KeyboardKeys.Control) && GetChildAt(location) is KeyframePoint keyframe) + { + keyframe.IsSelected = !keyframe.IsSelected; + _editor.UpdateTangents(); + } _isMovingSelection = false; _isMovingTangent = false; @@ -514,11 +543,11 @@ namespace FlaxEditor.GUI { if (base.OnMouseDoubleClick(location, button)) return true; - + // Add keyframe on double click var child = GetChildAt(location); - if (child is not KeyframePoint && - child is not TangentPoint && + if (child is not KeyframePoint && + child is not TangentPoint && _editor.KeyframesCount < _editor.MaxKeyframes) { var viewRect = _editor._mainPanel.GetClientArea(); @@ -545,7 +574,7 @@ namespace FlaxEditor.GUI var viewRect = _editor._mainPanel.GetClientArea(); var locationInKeyframes = PointToKeyframes(location, ref viewRect); var locationInEditorBefore = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect); - + // Scale relative to the curve size var scale = new Float2(delta * 0.1f); _editor._mainPanel.GetDesireClientArea(out var mainPanelArea); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index ed0885afd..9a774e519 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -163,10 +163,11 @@ namespace FlaxEditor.GUI /// public override void Draw() { + var style = Style.Current; var rect = new Rectangle(Float2.Zero, Size); - var color = Editor.ShowCollapsed ? Color.Gray : Editor.Colors[Component]; + var color = Editor.ShowCollapsed ? style.ForegroundDisabled : Editor.Colors[Component]; if (IsSelected) - color = Editor.ContainsFocus ? Color.YellowGreen : Color.Lerp(Color.Gray, Color.YellowGreen, 0.4f); + color = Editor.ContainsFocus ? style.SelectionBorder : Color.Lerp(style.ForegroundDisabled, style.SelectionBorder, 0.4f); if (IsMouseOver) color *= 1.1f; Render2D.FillRectangle(rect, color); @@ -244,14 +245,19 @@ namespace FlaxEditor.GUI set => Editor.SetKeyframeTangentInternal(Index, IsIn, Component, value); } + internal float TangentOffset => 50.0f / Editor.ViewScale.X; + /// public override void Draw() { + var style = Style.Current; + var thickness = 6.0f / Mathf.Max(Editor.ViewScale.X, 1.0f); + var size = Size; var pointPos = PointFromParent(Point.Center); - Render2D.DrawLine(Size * 0.5f, pointPos, Color.Gray); + Render2D.DrawLine(size * 0.5f, pointPos, style.ForegroundDisabled, thickness); - var rect = new Rectangle(Float2.Zero, Size); - var color = Color.MediumVioletRed; + var rect = new Rectangle(Float2.Zero, size); + var color = style.BorderSelected; if (IsMouseOver) color *= 1.1f; Render2D.FillRectangle(rect, color); @@ -289,7 +295,7 @@ namespace FlaxEditor.GUI /// /// The curve time/value axes tick steps. /// - protected float[] TickSteps = Utilities.Utils.CurveTickSteps; + protected double[] TickSteps = Utilities.Utils.CurveTickSteps; /// /// The curve contents area. @@ -442,7 +448,7 @@ namespace FlaxEditor.GUI _mainPanel = new Panel(ScrollBars.Both) { ScrollMargin = new Margin(150.0f), - AlwaysShowScrollbars = true, + AlwaysShowScrollbars = false, AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, Parent = this @@ -668,26 +674,82 @@ namespace FlaxEditor.GUI OnEditingEnd(); } + private void ShowCurve(bool selectedOnly) + { + if (_points.Count == 0) + return; + int pass = 1; + REDO: + + // Get curve bounds in Keyframes (time and value) + Float2 posMin = Float2.Maximum, posMax = Float2.Minimum; + // TODO: include bezier curve bounds calculation to handle curve outside the bounds made out of points + foreach (var point in _points) + { + if (selectedOnly && !point.IsSelected) + continue; + var pos = point.Point; + Float2.Min(ref posMin, ref pos, out posMin); + Float2.Max(ref posMax, ref pos, out posMax); + } + + // Apply margin around the area + var posMargin = (posMax - posMin) * 0.05f; + posMin -= posMargin; + posMax += posMargin; + + // Convert from Keyframes to Contents + _mainPanel.GetDesireClientArea(out var viewRect); + PointFromKeyframesToContents(ref posMin, ref viewRect); + PointFromKeyframesToContents(ref posMax, ref viewRect); + var tmp = posMin; + Float2.Min(ref posMin, ref posMax, out posMin); + Float2.Max(ref posMax, ref tmp, out posMax); + var contentsSize = posMax - posMin; + + // Convert from Contents to Main Panel + posMin = _contents.PointToParent(posMin); + posMax = _contents.PointToParent(posMax); + tmp = posMin; + Float2.Min(ref posMin, ref posMax, out posMin); + Float2.Max(ref posMax, ref tmp, out posMax); + + // Update zoom (leave unchanged when focusing a single point) + var zoomMask = EnableZoom; + if (Mathf.IsZero(posMargin.X)) + zoomMask &= ~UseMode.Horizontal; + if (Mathf.IsZero(posMargin.Y)) + zoomMask &= ~UseMode.Vertical; + ViewScale = ApplyUseModeMask(zoomMask, viewRect.Size / contentsSize, ViewScale); + + // Update scroll (attempt to center the area when it's smaller than the view) + Float2 viewOffset = -posMin; + Float2 viewSize = _mainPanel.Size; + Float2 viewSizeLeft = viewSize - Float2.Clamp(posMax - posMin, Float2.Zero, viewSize); + viewOffset += viewSizeLeft * 0.5f; + viewOffset = ApplyUseModeMask(EnablePanning, viewOffset, _mainPanel.ViewOffset); + _mainPanel.ViewOffset = viewOffset; + + // Do it multiple times so the view offset can be properly calculate once the view scale gets changes + if (pass++ <= 2) + goto REDO; + + UpdateKeyframes(); + } + + /// + /// Focuses the view on the selected keyframes. + /// + public void FocusSelection() + { + // Fallback to showing whole curve if nothing is selected + ShowCurve(SelectionCount != 0); + } + /// public override void ShowWholeCurve() { - _mainPanel.GetDesireClientArea(out var mainPanelArea); - ViewScale = ApplyUseModeMask(EnableZoom, mainPanelArea.Size / _contents.Size, ViewScale); - Float2 minPos = Float2.Maximum; - foreach (var point in _points) - { - var pos = point.PointToParent(point.Location); - Float2.Min(ref minPos, ref pos, out minPos); - } - var minPosPoint = _contents.PointToParent(ref minPos); - var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0); - scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll); - if (_mainPanel.HScrollBar != null) - _mainPanel.HScrollBar.TargetValue = scroll.X; - if (_mainPanel.VScrollBar != null) - _mainPanel.VScrollBar.TargetValue = scroll.Y; - - UpdateKeyframes(); + ShowCurve(false); } /// @@ -766,10 +828,7 @@ namespace FlaxEditor.GUI point = _contents.PointFromParent(point); // Contents -> Keyframes - return new Float2( - (point.X + _contents.Location.X) / UnitsPerSecond, - (point.Y + _contents.Location.Y - curveContentAreaBounds.Height) / -UnitsPerSecond - ); + return PointFromContentsToKeyframes(ref point, ref curveContentAreaBounds); } /// @@ -781,10 +840,7 @@ namespace FlaxEditor.GUI protected Float2 PointFromKeyframes(Float2 point, ref Rectangle curveContentAreaBounds) { // Keyframes -> Contents - point = new Float2( - point.X * UnitsPerSecond - _contents.Location.X, - point.Y * -UnitsPerSecond + curveContentAreaBounds.Height - _contents.Location.Y - ); + PointFromKeyframesToContents(ref point, ref curveContentAreaBounds); // Contents -> Main Panel point = _contents.PointToParent(point); @@ -793,11 +849,27 @@ namespace FlaxEditor.GUI return _mainPanel.PointToParent(point); } + internal Float2 PointFromContentsToKeyframes(ref Float2 point, ref Rectangle curveContentAreaBounds) + { + return new Float2( + (point.X + _contents.Location.X) / UnitsPerSecond, + (point.Y + _contents.Location.Y - curveContentAreaBounds.Height) / -UnitsPerSecond + ); + } + + internal void PointFromKeyframesToContents(ref Float2 point, ref Rectangle curveContentAreaBounds) + { + point = new Float2( + point.X * UnitsPerSecond - _contents.Location.X, + point.Y * -UnitsPerSecond + curveContentAreaBounds.Height - _contents.Location.Y + ); + } + private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) { - Utilities.Utils.DrawCurveTicks((float tick, float strength) => + Utilities.Utils.DrawCurveTicks((decimal tick, double step, float strength) => { - var p = PointFromKeyframes(axis * tick, ref viewRect); + var p = PointFromKeyframes(axis * (float)tick, ref viewRect); // Draw line var lineRect = new Rectangle @@ -820,6 +892,24 @@ namespace FlaxEditor.GUI }, TickSteps, ref _tickStrengths, min, max, pixelRange); } + private void SetupGrid(out Float2 min, out Float2 max, out Float2 pixelRange) + { + var viewRect = _mainPanel.GetClientArea(); + var upperLeft = PointToKeyframes(viewRect.Location, ref viewRect); + var bottomRight = PointToKeyframes(viewRect.Size, ref viewRect); + + min = Float2.Min(upperLeft, bottomRight); + max = Float2.Max(upperLeft, bottomRight); + pixelRange = (max - min) * ViewScale * UnitsPerSecond; + } + + private Float2 GetGridSnap() + { + SetupGrid(out var min, out var max, out var pixelRange); + return new Float2(Utilities.Utils.GetCurveGridSnap(TickSteps, ref _tickStrengths, min.X, max.X, pixelRange.X), + Utilities.Utils.GetCurveGridSnap(TickSteps, ref _tickStrengths, min.Y, max.Y, pixelRange.Y)); + } + /// /// Draws the curve. /// @@ -849,12 +939,7 @@ namespace FlaxEditor.GUI // Draw time and values axes if (ShowAxes != UseMode.Off) { - var upperLeft = PointToKeyframes(viewRect.Location, ref viewRect); - var bottomRight = PointToKeyframes(viewRect.Size, ref viewRect); - - var min = Float2.Min(upperLeft, bottomRight); - var max = Float2.Max(upperLeft, bottomRight); - var pixelRange = (max - min) * ViewScale * UnitsPerSecond; + SetupGrid(out var min, out var max, out var pixelRange); Render2D.PushClip(ref viewRect); @@ -939,7 +1024,7 @@ namespace FlaxEditor.GUI } else if (options.FocusSelection.Process(this)) { - ShowWholeCurve(); + FocusSelection(); return true; } @@ -2200,7 +2285,7 @@ namespace FlaxEditor.GUI var tangent = t.TangentValue; var direction = t.IsIn ? -1.0f : 1.0f; - var offset = 30.0f; + var offset = t.TangentOffset; var location = GetKeyframePoint(ref k, selectedComponent); t.Size = KeyframesSize / ViewScale; t.Location = new Float2 @@ -2227,6 +2312,18 @@ namespace FlaxEditor.GUI } } + /// + protected override void SetScaleInternal(ref Float2 scale) + { + base.SetScaleInternal(ref scale); + + if (!_showCollapsed) + { + // Refresh keyframes when zooming (their size depends on the scale) + UpdateKeyframes(); + } + } + /// protected override void OnShowContextMenu(ContextMenu.ContextMenu cm, int selectionCount) { diff --git a/Source/Editor/GUI/Popups/ActorSearchPopup.cs b/Source/Editor/GUI/Popups/ActorSearchPopup.cs index 2c327b685..7be17db98 100644 --- a/Source/Editor/GUI/Popups/ActorSearchPopup.cs +++ b/Source/Editor/GUI/Popups/ActorSearchPopup.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.Windows; +using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; @@ -55,18 +57,26 @@ namespace FlaxEditor.GUI private IsValidDelegate _isValid; private Action _selected; - private ActorSearchPopup(IsValidDelegate isValid, Action selected) + private ActorSearchPopup(IsValidDelegate isValid, Action selected, CustomEditors.IPresenterOwner context) { _isValid = isValid; _selected = selected; ItemClicked += OnItemClicked; - // TODO: use async thread to search scenes - for (int i = 0; i < Level.ScenesCount; i++) + if (context is PropertiesWindow propertiesWindow || context == null) { - Find(Level.GetScene(i)); + // TODO: use async thread to search scenes + for (int i = 0; i < Level.ScenesCount; i++) + { + Find(Level.GetScene(i)); + } } + else if (context is PrefabWindow prefabWindow) + { + Find(prefabWindow.Graph.MainActor); + } + SortItems(); } @@ -98,10 +108,11 @@ namespace FlaxEditor.GUI /// The show target location. /// Event called to check if a given actor item is valid to be used. /// Event called on actor item pick. + /// The presenter owner context (i.e. PrefabWindow, PropertiesWindow). /// The dialog. - public static ActorSearchPopup Show(Control showTarget, Float2 showTargetLocation, IsValidDelegate isValid, Action selected) + public static ActorSearchPopup Show(Control showTarget, Float2 showTargetLocation, IsValidDelegate isValid, Action selected, CustomEditors.IPresenterOwner context) { - var popup = new ActorSearchPopup(isValid, selected); + var popup = new ActorSearchPopup(isValid, selected, context); popup.Show(showTarget, showTargetLocation); return popup; } diff --git a/Source/Editor/GUI/Popups/ScriptSearchPopup.cs b/Source/Editor/GUI/Popups/ScriptSearchPopup.cs index 93c860275..a6414bb86 100644 --- a/Source/Editor/GUI/Popups/ScriptSearchPopup.cs +++ b/Source/Editor/GUI/Popups/ScriptSearchPopup.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.Windows; +using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; @@ -66,18 +68,26 @@ namespace FlaxEditor.GUI private IsValidDelegate _isValid; private Action