Merge remote-tracking branch 'upstream/master'
This commit is contained in:
4
.github/workflows/build_android.yml
vendored
4
.github/workflows/build_android.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Setup .NET Workload
|
- name: Setup .NET Workload
|
||||||
run: |
|
run: |
|
||||||
dotnet workload install android
|
dotnet workload install android
|
||||||
@@ -33,4 +33,4 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=7 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame
|
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Android -configuration=Release -buildtargets=FlaxGame
|
||||||
|
|||||||
4
.github/workflows/build_ios.yml
vendored
4
.github/workflows/build_ios.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Setup .NET Workload
|
- name: Setup .NET Workload
|
||||||
run: |
|
run: |
|
||||||
dotnet workload install ios
|
dotnet workload install ios
|
||||||
@@ -33,4 +33,4 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -dotnet=7 -arch=ARM64 -platform=iOS -configuration=Release -buildtargets=FlaxGame
|
./Development/Scripts/Mac/CallBuildTool.sh -build -log -dotnet=8 -arch=ARM64 -platform=iOS -configuration=Release -buildtargets=FlaxGame
|
||||||
|
|||||||
8
.github/workflows/build_linux.yml
vendored
8
.github/workflows/build_linux.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./Development/Scripts/Linux/CallBuildTool.sh -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxEditor
|
./Development/Scripts/Linux/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxEditor
|
||||||
|
|
||||||
# Game
|
# Game
|
||||||
game-linux:
|
game-linux:
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -64,4 +64,4 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./Development/Scripts/Linux/CallBuildTool.sh -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Linux -configuration=Release -buildtargets=FlaxGame
|
./Development/Scripts/Linux/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Linux -configuration=Release -buildtargets=FlaxGame
|
||||||
|
|||||||
8
.github/workflows/build_mac.yml
vendored
8
.github/workflows/build_mac.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor
|
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor
|
||||||
|
|
||||||
# Game
|
# Game
|
||||||
game-mac:
|
game-mac:
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -55,4 +55,4 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Mac -configuration=Release -buildtargets=FlaxGame
|
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Mac -configuration=Release -buildtargets=FlaxGame
|
||||||
|
|||||||
8
.github/workflows/build_windows.yml
vendored
8
.github/workflows/build_windows.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor
|
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor
|
||||||
|
|
||||||
# Game
|
# Game
|
||||||
game-windows:
|
game-windows:
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -55,4 +55,4 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=7 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame
|
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Release -buildtargets=FlaxGame
|
||||||
|
|||||||
12
.github/workflows/cd.yml
vendored
12
.github/workflows/cd.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -95,7 +95,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -159,7 +159,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -187,7 +187,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
|
|||||||
22
.github/workflows/tests.yml
vendored
22
.github/workflows/tests.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -34,21 +34,21 @@ jobs:
|
|||||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=7
|
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||||
./Development/Scripts/Linux/CallBuildTool.sh -build -log -dotnet=7 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxTestsTarget
|
./Development/Scripts/Linux/CallBuildTool.sh -build -log -dotnet=8 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxTestsTarget
|
||||||
dotnet msbuild Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo
|
dotnet msbuild Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo
|
||||||
dotnet msbuild Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo
|
dotnet msbuild Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
${GITHUB_WORKSPACE}/Binaries/Editor/Linux/Development/FlaxTests
|
${GITHUB_WORKSPACE}/Binaries/Editor/Linux/Development/FlaxTests
|
||||||
dotnet test -f net7.0 Binaries/Tests/Flax.Build.Tests.dll
|
dotnet test -f net8.0 Binaries/Tests/Flax.Build.Tests.dll
|
||||||
cp Binaries/Editor/Linux/Development/FlaxEngine.CSharp.dll Binaries/Tests
|
cp Binaries/Editor/Linux/Development/FlaxEngine.CSharp.dll Binaries/Tests
|
||||||
cp Binaries/Editor/Linux/Development/FlaxEngine.CSharp.runtimeconfig.json Binaries/Tests
|
cp Binaries/Editor/Linux/Development/FlaxEngine.CSharp.runtimeconfig.json Binaries/Tests
|
||||||
cp Binaries/Editor/Linux/Development/Newtonsoft.Json.dll Binaries/Tests
|
cp Binaries/Editor/Linux/Development/Newtonsoft.Json.dll Binaries/Tests
|
||||||
dotnet test -f net7.0 Binaries/Tests/FlaxEngine.CSharp.dll
|
dotnet test -f net8.0 Binaries/Tests/FlaxEngine.CSharp.dll
|
||||||
- name: Test UseLargeWorlds
|
- name: Test UseLargeWorlds
|
||||||
run: |
|
run: |
|
||||||
./Development/Scripts/Linux/CallBuildTool.sh -build -log -dotnet=7 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxTestsTarget -UseLargeWorlds=true
|
./Development/Scripts/Linux/CallBuildTool.sh -build -log -dotnet=8 -arch=x64 -platform=Linux -configuration=Development -buildtargets=FlaxTestsTarget -UseLargeWorlds=true
|
||||||
${GITHUB_WORKSPACE}/Binaries/Editor/Linux/Development/FlaxTests
|
${GITHUB_WORKSPACE}/Binaries/Editor/Linux/Development/FlaxTests
|
||||||
|
|
||||||
# Tests on Windows
|
# Tests on Windows
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Print .NET info
|
- name: Print .NET info
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
@@ -72,14 +72,14 @@ jobs:
|
|||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
.\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=7
|
.\GenerateProjectFiles.bat -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||||
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=7 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget
|
.\Development\Scripts\Windows\CallBuildTool.bat -build -log -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxTestsTarget
|
||||||
dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo
|
dotnet msbuild Source\Tools\Flax.Build.Tests\Flax.Build.Tests.csproj /m /t:Restore,Build /p:Configuration=Debug /p:Platform=AnyCPU /nologo
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
.\Binaries\Editor\Win64\Development\FlaxTests.exe
|
.\Binaries\Editor\Win64\Development\FlaxTests.exe
|
||||||
dotnet test -f net7.0 Binaries\Tests\Flax.Build.Tests.dll
|
dotnet test -f net8.0 Binaries\Tests\Flax.Build.Tests.dll
|
||||||
xcopy /y Binaries\Editor\Win64\Development\FlaxEngine.CSharp.dll Binaries\Tests
|
xcopy /y Binaries\Editor\Win64\Development\FlaxEngine.CSharp.dll Binaries\Tests
|
||||||
xcopy /y Binaries\Editor\Win64\Development\FlaxEngine.CSharp.runtimeconfig.json Binaries\Tests
|
xcopy /y Binaries\Editor\Win64\Development\FlaxEngine.CSharp.runtimeconfig.json Binaries\Tests
|
||||||
xcopy /y Binaries\Editor\Win64\Development\Newtonsoft.Json.dll Binaries\Tests
|
xcopy /y Binaries\Editor\Win64\Development\Newtonsoft.Json.dll Binaries\Tests
|
||||||
dotnet test -f net7.0 Binaries\Tests\FlaxEngine.CSharp.dll
|
dotnet test -f net8.0 Binaries\Tests\FlaxEngine.CSharp.dll
|
||||||
|
|||||||
BIN
Content/Shaders/DebugDraw.flax
(Stored with Git LFS)
BIN
Content/Shaders/DebugDraw.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/TAA.flax
(Stored with Git LFS)
BIN
Content/Shaders/TAA.flax
(Stored with Git LFS)
Binary file not shown.
@@ -2,9 +2,9 @@
|
|||||||
"Name": "Flax",
|
"Name": "Flax",
|
||||||
"Version": {
|
"Version": {
|
||||||
"Major": 1,
|
"Major": 1,
|
||||||
"Minor": 7,
|
"Minor": 8,
|
||||||
"Revision": 2,
|
"Revision": 0,
|
||||||
"Build": 6408
|
"Build": 6510
|
||||||
},
|
},
|
||||||
"Company": "Flax",
|
"Company": "Flax",
|
||||||
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
|
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pushd
|
|||||||
echo Performing the full package...
|
echo Performing the full package...
|
||||||
|
|
||||||
rem Run the build tool.
|
rem Run the build tool.
|
||||||
call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployEditor -deployPlatforms -dotnet=7 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %*
|
call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployEditor -deployPlatforms -dotnet=8 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %*
|
||||||
if errorlevel 1 goto BuildToolFailed
|
if errorlevel 1 goto BuildToolFailed
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pushd
|
|||||||
echo Building and packaging Flax Editor...
|
echo Building and packaging Flax Editor...
|
||||||
|
|
||||||
rem Run the build tool.
|
rem Run the build tool.
|
||||||
call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployEditor -dotnet=7 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %*
|
call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployEditor -dotnet=8 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %*
|
||||||
if errorlevel 1 goto BuildToolFailed
|
if errorlevel 1 goto BuildToolFailed
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ echo Building and packaging Flax Editor...
|
|||||||
cd "`dirname "$0"`"
|
cd "`dirname "$0"`"
|
||||||
|
|
||||||
# Run Flax.Build (also pass the arguments)
|
# Run Flax.Build (also pass the arguments)
|
||||||
bash ./Development/Scripts/Mac/CallBuildTool.sh --deploy --deployEditor --dotnet=7 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@"
|
bash ./Development/Scripts/Mac/CallBuildTool.sh --deploy --deployEditor --dotnet=8 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@"
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ echo Building and packaging Flax Editor...
|
|||||||
cd "`dirname "$0"`"
|
cd "`dirname "$0"`"
|
||||||
|
|
||||||
# Run Flax.Build (also pass the arguments)
|
# Run Flax.Build (also pass the arguments)
|
||||||
bash ./Development/Scripts/Linux/CallBuildTool.sh --deploy --deployEditor --dotnet=7 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@"
|
bash ./Development/Scripts/Linux/CallBuildTool.sh --deploy --deployEditor --dotnet=8 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pushd
|
|||||||
echo Building and packaging platforms data...
|
echo Building and packaging platforms data...
|
||||||
|
|
||||||
rem Run the build tool.
|
rem Run the build tool.
|
||||||
call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployPlatforms -dotnet=7 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %*
|
call "Development\Scripts\Windows\CallBuildTool.bat" -deploy -deployPlatforms -dotnet=8 -verbose -log -logFile="Cache\Intermediate\PackageLog.txt" %*
|
||||||
if errorlevel 1 goto BuildToolFailed
|
if errorlevel 1 goto BuildToolFailed
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ echo Building and packaging platforms data...
|
|||||||
cd "`dirname "$0"`"
|
cd "`dirname "$0"`"
|
||||||
|
|
||||||
# Run Flax.Build (also pass the arguments)
|
# Run Flax.Build (also pass the arguments)
|
||||||
bash ./Development/Scripts/Mac/CallBuildTool.sh --deploy --deployPlatforms --dotnet=7 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@"
|
bash ./Development/Scripts/Mac/CallBuildTool.sh --deploy --deployPlatforms --dotnet=8 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@"
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ echo Building and packaging platforms data...
|
|||||||
cd "`dirname "$0"`"
|
cd "`dirname "$0"`"
|
||||||
|
|
||||||
# Run Flax.Build (also pass the arguments)
|
# Run Flax.Build (also pass the arguments)
|
||||||
bash ./Development/Scripts/Linux/CallBuildTool.sh --deploy --deployPlatforms --dotnet=7 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@"
|
bash ./Development/Scripts/Linux/CallBuildTool.sh --deploy --deployPlatforms --dotnet=8 --verbose --log --logFile="Cache/Intermediate/PackageLog.txt" "$@"
|
||||||
|
|||||||
12
README.md
12
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 Visual Studio 2022 or newer
|
||||||
* Install Windows 8.1 SDK or newer (via Visual Studio Installer)
|
* 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 Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer)
|
||||||
* Install .NET 7 SDK for **Windows x64** (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
* 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 Git with LFS
|
* Install Git with LFS
|
||||||
* Clone repo (with LFS)
|
* Clone repo (with LFS)
|
||||||
* Run **GenerateProjectFiles.bat**
|
* Run **GenerateProjectFiles.bat**
|
||||||
@@ -44,8 +44,8 @@ Follow the instructions below to compile and run the engine from source.
|
|||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
* Install Visual Studio Code
|
* Install Visual Studio Code
|
||||||
* Install .NET 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
* Install .NET 8 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-7.0`
|
* Ubuntu: `sudo apt install dotnet-sdk-8.0`
|
||||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||||
* Ubuntu: `sudo apt install vulkan-sdk`
|
* Ubuntu: `sudo apt install vulkan-sdk`
|
||||||
* Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers`
|
* Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||||
@@ -67,7 +67,7 @@ Follow the instructions below to compile and run the engine from source.
|
|||||||
## Mac
|
## Mac
|
||||||
|
|
||||||
* Install XCode
|
* Install XCode
|
||||||
* Install .NET 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0))
|
* 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 Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||||
* Clone repo (with LFS)
|
* Clone repo (with LFS)
|
||||||
* Run `GenerateProjectFiles.command`
|
* Run `GenerateProjectFiles.command`
|
||||||
@@ -80,9 +80,9 @@ Follow the instructions below to compile and run the engine from source.
|
|||||||
|
|
||||||
Restart PC - ensure DotNet is added to PATH for command line tools execution.
|
Restart PC - ensure DotNet is added to PATH for command line tools execution.
|
||||||
|
|
||||||
* `Microsoft.NET.TargetFrameworkInference.targets(141,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 7.0. Either target .NET 5.0 or lower, or use a version of the .NET SDK that supports .NET 7.0`
|
* `Microsoft.NET.TargetFrameworkInference.targets(141,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 8.0. Either target .NET 5.0 or lower, or use a version of the .NET SDK that supports .NET 8.0`
|
||||||
|
|
||||||
Use Visual Studio 2022, older versions are not supported by .NET SDK 7.
|
Use Visual Studio 2022, older versions are not supported by .NET SDK 8.
|
||||||
|
|
||||||
* `Building for Windows without Vulkan rendering backend (Vulkan SDK is missing)`
|
* `Building for Windows without Vulkan rendering backend (Vulkan SDK is missing)`
|
||||||
|
|
||||||
|
|||||||
@@ -646,7 +646,9 @@ namespace FlaxEditor.Content.GUI
|
|||||||
_rubberBandRectangle = new Rectangle(_mousePressLocation, 0, 0);
|
_rubberBandRectangle = new Rectangle(_mousePressLocation, 0, 0);
|
||||||
_isRubberBandSpanning = true;
|
_isRubberBandSpanning = true;
|
||||||
StartMouseCapture();
|
StartMouseCapture();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return AutoFocus && Focus(this);
|
return AutoFocus && Focus(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,8 @@ namespace FlaxEditor.Content
|
|||||||
if (_attributes == null)
|
if (_attributes == null)
|
||||||
{
|
{
|
||||||
var data = _type.Asset.GetMethodMetaData(_index, Surface.SurfaceMeta.AttributeMetaTypeID);
|
var data = _type.Asset.GetMethodMetaData(_index, Surface.SurfaceMeta.AttributeMetaTypeID);
|
||||||
_attributes = Surface.SurfaceMeta.GetAttributes(data);
|
var dataOld = _type.Asset.GetMethodMetaData(_index, Surface.SurfaceMeta.OldAttributeMetaTypeID);
|
||||||
|
_attributes = Surface.SurfaceMeta.GetAttributes(data, dataOld);
|
||||||
}
|
}
|
||||||
return _attributes;
|
return _attributes;
|
||||||
}
|
}
|
||||||
@@ -290,13 +291,11 @@ namespace FlaxEditor.Content
|
|||||||
_methods = Utils.GetEmptyArray<ScriptMemberInfo>();
|
_methods = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||||
|
|
||||||
// Cache Visual Script attributes
|
// Cache Visual Script attributes
|
||||||
var attributesData = _asset.GetMetaData(Surface.SurfaceMeta.AttributeMetaTypeID);
|
|
||||||
if (attributesData != null && attributesData.Length != 0)
|
|
||||||
{
|
{
|
||||||
_attributes = Surface.SurfaceMeta.GetAttributes(attributesData);
|
var data = _asset.GetMetaData(Surface.SurfaceMeta.AttributeMetaTypeID);
|
||||||
|
var dataOld = _asset.GetMetaData(Surface.SurfaceMeta.OldAttributeMetaTypeID);
|
||||||
|
_attributes = Surface.SurfaceMeta.GetAttributes(data, dataOld);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
_attributes = Utils.GetEmptyArray<object>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAssetReloading(Asset asset)
|
private void OnAssetReloading(Asset asset)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using FlaxEditor.Content.Thumbnails;
|
using FlaxEditor.Content.Thumbnails;
|
||||||
using FlaxEditor.Viewport.Previews;
|
using FlaxEditor.Viewport.Previews;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
@@ -194,4 +195,64 @@ namespace FlaxEditor.Content
|
|||||||
base.Dispose();
|
base.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Content proxy for quick UI Control prefab creation as widget.
|
||||||
|
/// </summary>
|
||||||
|
[ContentContextMenu("New/Widget")]
|
||||||
|
internal sealed class WidgetProxy : AssetProxy
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Name => "UI Widget";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool IsProxyFor(ContentItem item)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string FileExtension => PrefabProxy.Extension;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Color AccentColor => Color.Transparent;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string TypeName => PrefabProxy.AssetTypename;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanCreate(ContentFolder targetLocation)
|
||||||
|
{
|
||||||
|
return targetLocation.CanHaveAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Create(string outputPath, object arg)
|
||||||
|
{
|
||||||
|
// Create prefab with UI Control
|
||||||
|
var actor = new UIControl
|
||||||
|
{
|
||||||
|
Name = Path.GetFileNameWithoutExtension(outputPath),
|
||||||
|
StaticFlags = StaticFlags.None,
|
||||||
|
};
|
||||||
|
actor.Control = new Button
|
||||||
|
{
|
||||||
|
Text = "Button",
|
||||||
|
};
|
||||||
|
PrefabManager.CreatePrefab(actor, outputPath, false);
|
||||||
|
Object.Destroy(actor, 20.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class PlatformTools;
|
|||||||
|
|
||||||
#if OFFICIAL_BUILD
|
#if OFFICIAL_BUILD
|
||||||
// Use the fixed .NET SDK version in packaged builds for compatibility (FlaxGame is precompiled with it)
|
// Use the fixed .NET SDK version in packaged builds for compatibility (FlaxGame is precompiled with it)
|
||||||
#define GAME_BUILD_DOTNET_VER TEXT("-dotnet=7")
|
#define GAME_BUILD_DOTNET_VER TEXT("-dotnet=8")
|
||||||
#else
|
#else
|
||||||
#define GAME_BUILD_DOTNET_VER TEXT("")
|
#define GAME_BUILD_DOTNET_VER TEXT("")
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ IMPLEMENT_ENGINE_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform);
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
struct AndroidPlatformCache
|
||||||
|
{
|
||||||
|
AndroidPlatformSettings::TextureQuality TexturesQuality;
|
||||||
|
};
|
||||||
|
|
||||||
void DeployIcon(const CookingData& data, const TextureData& iconData, const Char* subDir, int32 iconSize, int32 adaptiveIconSize)
|
void DeployIcon(const CookingData& data, const TextureData& iconData, const Char* subDir, int32 iconSize, int32 adaptiveIconSize)
|
||||||
{
|
{
|
||||||
const String mipmapPath = data.OriginalOutputPath / TEXT("app/src/main/res") / subDir;
|
const String mipmapPath = data.OriginalOutputPath / TEXT("app/src/main/res") / subDir;
|
||||||
@@ -30,6 +35,24 @@ namespace
|
|||||||
FileSystem::CreateDirectory(mipmapPath);
|
FileSystem::CreateDirectory(mipmapPath);
|
||||||
EditorUtilities::ExportApplicationImage(iconData, iconSize, iconSize, PixelFormat::B8G8R8A8_UNorm, iconPath);
|
EditorUtilities::ExportApplicationImage(iconData, iconSize, iconSize, PixelFormat::B8G8R8A8_UNorm, iconPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PixelFormat GetQualityTextureFormat(bool sRGB, PixelFormat format)
|
||||||
|
{
|
||||||
|
const auto platformSettings = AndroidPlatformSettings::Get();
|
||||||
|
switch (platformSettings->TexturesQuality)
|
||||||
|
{
|
||||||
|
case AndroidPlatformSettings::TextureQuality::Uncompressed:
|
||||||
|
return PixelFormatExtensions::FindUncompressedFormat(format);
|
||||||
|
case AndroidPlatformSettings::TextureQuality::ASTC_High:
|
||||||
|
return sRGB ? PixelFormat::ASTC_4x4_UNorm_sRGB : PixelFormat::ASTC_4x4_UNorm;
|
||||||
|
case AndroidPlatformSettings::TextureQuality::ASTC_Medium:
|
||||||
|
return sRGB ? PixelFormat::ASTC_6x6_UNorm_sRGB : PixelFormat::ASTC_6x6_UNorm;
|
||||||
|
case AndroidPlatformSettings::TextureQuality::ASTC_Low:
|
||||||
|
return sRGB ? PixelFormat::ASTC_8x8_UNorm_sRGB : PixelFormat::ASTC_8x8_UNorm;
|
||||||
|
default:
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Char* AndroidPlatformTools::GetDisplayName() const
|
const Char* AndroidPlatformTools::GetDisplayName() const
|
||||||
@@ -54,62 +77,67 @@ ArchitectureType AndroidPlatformTools::GetArchitecture() const
|
|||||||
|
|
||||||
PixelFormat AndroidPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format)
|
PixelFormat AndroidPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format)
|
||||||
{
|
{
|
||||||
// TODO: add ETC compression support for Android
|
|
||||||
// TODO: add ASTC compression support for Android
|
|
||||||
|
|
||||||
// BC formats are not widely supported on Android
|
|
||||||
if (PixelFormatExtensions::IsCompressedBC(format))
|
|
||||||
{
|
|
||||||
switch (format)
|
|
||||||
{
|
|
||||||
case PixelFormat::BC1_Typeless:
|
|
||||||
case PixelFormat::BC2_Typeless:
|
|
||||||
case PixelFormat::BC3_Typeless:
|
|
||||||
return PixelFormat::R8G8B8A8_Typeless;
|
|
||||||
case PixelFormat::BC1_UNorm:
|
|
||||||
case PixelFormat::BC2_UNorm:
|
|
||||||
case PixelFormat::BC3_UNorm:
|
|
||||||
return PixelFormat::R8G8B8A8_UNorm;
|
|
||||||
case PixelFormat::BC1_UNorm_sRGB:
|
|
||||||
case PixelFormat::BC2_UNorm_sRGB:
|
|
||||||
case PixelFormat::BC3_UNorm_sRGB:
|
|
||||||
return PixelFormat::R8G8B8A8_UNorm_sRGB;
|
|
||||||
case PixelFormat::BC4_Typeless:
|
|
||||||
return PixelFormat::R8_Typeless;
|
|
||||||
case PixelFormat::BC4_UNorm:
|
|
||||||
return PixelFormat::R8_UNorm;
|
|
||||||
case PixelFormat::BC4_SNorm:
|
|
||||||
return PixelFormat::R8_SNorm;
|
|
||||||
case PixelFormat::BC5_Typeless:
|
|
||||||
return PixelFormat::R16G16_Typeless;
|
|
||||||
case PixelFormat::BC5_UNorm:
|
|
||||||
return PixelFormat::R16G16_UNorm;
|
|
||||||
case PixelFormat::BC5_SNorm:
|
|
||||||
return PixelFormat::R16G16_SNorm;
|
|
||||||
case PixelFormat::BC7_Typeless:
|
|
||||||
case PixelFormat::BC6H_Typeless:
|
|
||||||
return PixelFormat::R16G16B16A16_Typeless;
|
|
||||||
case PixelFormat::BC7_UNorm:
|
|
||||||
case PixelFormat::BC6H_Uf16:
|
|
||||||
case PixelFormat::BC6H_Sf16:
|
|
||||||
return PixelFormat::R16G16B16A16_Float;
|
|
||||||
case PixelFormat::BC7_UNorm_sRGB:
|
|
||||||
return PixelFormat::R16G16B16A16_UNorm;
|
|
||||||
default:
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
// Not all Android devices support R11G11B10 textures (eg. M6 Note)
|
|
||||||
case PixelFormat::R11G11B10_Float:
|
case PixelFormat::R11G11B10_Float:
|
||||||
|
// Not all Android devices support R11G11B10 textures (eg. M6 Note)
|
||||||
return PixelFormat::R16G16B16A16_UNorm;
|
return PixelFormat::R16G16B16A16_UNorm;
|
||||||
|
case PixelFormat::BC1_Typeless:
|
||||||
|
case PixelFormat::BC2_Typeless:
|
||||||
|
case PixelFormat::BC3_Typeless:
|
||||||
|
case PixelFormat::BC4_Typeless:
|
||||||
|
case PixelFormat::BC5_Typeless:
|
||||||
|
case PixelFormat::BC1_UNorm:
|
||||||
|
case PixelFormat::BC2_UNorm:
|
||||||
|
case PixelFormat::BC3_UNorm:
|
||||||
|
case PixelFormat::BC4_UNorm:
|
||||||
|
case PixelFormat::BC5_UNorm:
|
||||||
|
return GetQualityTextureFormat(false, format);
|
||||||
|
case PixelFormat::BC1_UNorm_sRGB:
|
||||||
|
case PixelFormat::BC2_UNorm_sRGB:
|
||||||
|
case PixelFormat::BC3_UNorm_sRGB:
|
||||||
|
case PixelFormat::BC7_UNorm_sRGB:
|
||||||
|
return GetQualityTextureFormat(true, format);
|
||||||
|
case PixelFormat::BC4_SNorm:
|
||||||
|
return PixelFormat::R8_SNorm;
|
||||||
|
case PixelFormat::BC5_SNorm:
|
||||||
|
return PixelFormat::R16G16_SNorm;
|
||||||
|
case PixelFormat::BC6H_Typeless:
|
||||||
|
case PixelFormat::BC6H_Uf16:
|
||||||
|
case PixelFormat::BC6H_Sf16:
|
||||||
|
case PixelFormat::BC7_Typeless:
|
||||||
|
case PixelFormat::BC7_UNorm:
|
||||||
|
return PixelFormat::R16G16B16A16_Float; // TODO: ASTC HDR
|
||||||
default:
|
default:
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidPlatformTools::LoadCache(CookingData& data, IBuildCache* cache, const Span<byte>& bytes)
|
||||||
|
{
|
||||||
|
const auto platformSettings = AndroidPlatformSettings::Get();
|
||||||
|
bool invalidTextures = true;
|
||||||
|
if (bytes.Length() == sizeof(AndroidPlatformCache))
|
||||||
|
{
|
||||||
|
auto* platformCache = (AndroidPlatformCache*)bytes.Get();
|
||||||
|
invalidTextures = platformCache->TexturesQuality != platformSettings->TexturesQuality;
|
||||||
|
}
|
||||||
|
if (invalidTextures)
|
||||||
|
{
|
||||||
|
LOG(Info, "{0} option has been modified.", TEXT("TexturesQuality"));
|
||||||
|
cache->InvalidateCacheTextures();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<byte> AndroidPlatformTools::SaveCache(CookingData& data, IBuildCache* cache)
|
||||||
|
{
|
||||||
|
const auto platformSettings = AndroidPlatformSettings::Get();
|
||||||
|
AndroidPlatformCache platformCache;
|
||||||
|
platformCache.TexturesQuality = platformSettings->TexturesQuality;
|
||||||
|
Array<byte> result;
|
||||||
|
result.Add((const byte*)&platformCache, sizeof(platformCache));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
void AndroidPlatformTools::OnBuildStarted(CookingData& data)
|
void AndroidPlatformTools::OnBuildStarted(CookingData& data)
|
||||||
{
|
{
|
||||||
// Adjust the cooking output folder to be located inside the Gradle assets directory
|
// Adjust the cooking output folder to be located inside the Gradle assets directory
|
||||||
@@ -328,7 +356,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
|
|||||||
|
|
||||||
// Copy result package
|
// 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 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 / gameSettings->ProductName + TEXT(".apk");
|
const String outputApk = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".apk");
|
||||||
if (FileSystem::CopyFile(outputApk, apk))
|
if (FileSystem::CopyFile(outputApk, apk))
|
||||||
{
|
{
|
||||||
LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk);
|
LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public:
|
|||||||
PlatformType GetPlatform() const override;
|
PlatformType GetPlatform() const override;
|
||||||
ArchitectureType GetArchitecture() const override;
|
ArchitectureType GetArchitecture() const override;
|
||||||
PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override;
|
PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override;
|
||||||
|
void LoadCache(CookingData& data, IBuildCache* cache, const Span<byte>& bytes) override;
|
||||||
|
Array<byte> SaveCache(CookingData& data, IBuildCache* cache) override;
|
||||||
void OnBuildStarted(CookingData& data) override;
|
void OnBuildStarted(CookingData& data) override;
|
||||||
bool OnPostProcess(CookingData& data) override;
|
bool OnPostProcess(CookingData& data) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -494,11 +494,11 @@ bool WindowsPlatformTools::OnDeployBinaries(CookingData& data)
|
|||||||
{
|
{
|
||||||
const auto platformSettings = WindowsPlatformSettings::Get();
|
const auto platformSettings = WindowsPlatformSettings::Get();
|
||||||
|
|
||||||
// Apply executable icon
|
|
||||||
Array<String> files;
|
Array<String> files;
|
||||||
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*.exe"), DirectorySearchOption::TopDirectoryOnly);
|
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*.exe"), DirectorySearchOption::TopDirectoryOnly);
|
||||||
if (files.HasItems())
|
if (files.HasItems())
|
||||||
{
|
{
|
||||||
|
// Apply executable icon
|
||||||
TextureData iconData;
|
TextureData iconData;
|
||||||
if (!EditorUtilities::GetApplicationImage(platformSettings->OverrideIcon, iconData))
|
if (!EditorUtilities::GetApplicationImage(platformSettings->OverrideIcon, iconData))
|
||||||
{
|
{
|
||||||
@@ -508,11 +508,31 @@ bool WindowsPlatformTools::OnDeployBinaries(CookingData& data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rename app
|
||||||
|
const String newName = EditorUtilities::GetOutputName();
|
||||||
|
if (newName != StringUtils::GetFileNameWithoutExtension(files[0]))
|
||||||
|
{
|
||||||
|
if (FileSystem::MoveFile(data.NativeCodeOutputPath / newName + TEXT(".exe"), files[0], true))
|
||||||
|
{
|
||||||
|
data.Error(TEXT("Failed to change output executable name."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowsPlatformTools::OnBuildStarted(CookingData& data)
|
||||||
|
{
|
||||||
|
// Remove old executable
|
||||||
|
Array<String> files;
|
||||||
|
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*.exe"), DirectorySearchOption::TopDirectoryOnly);
|
||||||
|
for (auto& file : files)
|
||||||
|
FileSystem::DeleteFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
void WindowsPlatformTools::OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir)
|
void WindowsPlatformTools::OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir)
|
||||||
{
|
{
|
||||||
// Pick the first executable file
|
// Pick the first executable file
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public:
|
|||||||
ArchitectureType GetArchitecture() const override;
|
ArchitectureType GetArchitecture() const override;
|
||||||
bool UseSystemDotnet() const override;
|
bool UseSystemDotnet() const override;
|
||||||
bool OnDeployBinaries(CookingData& data) override;
|
bool OnDeployBinaries(CookingData& data) override;
|
||||||
|
void OnBuildStarted(CookingData& data) override;
|
||||||
void OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir) override;
|
void OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ IMPLEMENT_SETTINGS_GETTER(iOSPlatformSettings, iOSPlatform);
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
struct iOSPlatformCache
|
||||||
|
{
|
||||||
|
iOSPlatformSettings::TextureQuality TexturesQuality;
|
||||||
|
};
|
||||||
|
|
||||||
String GetAppName()
|
String GetAppName()
|
||||||
{
|
{
|
||||||
const auto gameSettings = GameSettings::Get();
|
const auto gameSettings = GameSettings::Get();
|
||||||
@@ -60,6 +65,24 @@ namespace
|
|||||||
result = result.TrimTrailing();
|
result = result.TrimTrailing();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PixelFormat GetQualityTextureFormat(bool sRGB, PixelFormat format)
|
||||||
|
{
|
||||||
|
const auto platformSettings = iOSPlatformSettings::Get();
|
||||||
|
switch (platformSettings->TexturesQuality)
|
||||||
|
{
|
||||||
|
case iOSPlatformSettings::TextureQuality::Uncompressed:
|
||||||
|
return PixelFormatExtensions::FindUncompressedFormat(format);
|
||||||
|
case iOSPlatformSettings::TextureQuality::ASTC_High:
|
||||||
|
return sRGB ? PixelFormat::ASTC_4x4_UNorm_sRGB : PixelFormat::ASTC_4x4_UNorm;
|
||||||
|
case iOSPlatformSettings::TextureQuality::ASTC_Medium:
|
||||||
|
return sRGB ? PixelFormat::ASTC_6x6_UNorm_sRGB : PixelFormat::ASTC_6x6_UNorm;
|
||||||
|
case iOSPlatformSettings::TextureQuality::ASTC_Low:
|
||||||
|
return sRGB ? PixelFormat::ASTC_8x8_UNorm_sRGB : PixelFormat::ASTC_8x8_UNorm;
|
||||||
|
default:
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Char* iOSPlatformTools::GetDisplayName() const
|
const Char* iOSPlatformTools::GetDisplayName() const
|
||||||
@@ -89,51 +112,37 @@ DotNetAOTModes iOSPlatformTools::UseAOT() const
|
|||||||
|
|
||||||
PixelFormat iOSPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format)
|
PixelFormat iOSPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format)
|
||||||
{
|
{
|
||||||
// TODO: add ETC compression support for iOS
|
switch (format)
|
||||||
// TODO: add ASTC compression support for iOS
|
|
||||||
|
|
||||||
if (PixelFormatExtensions::IsCompressedBC(format))
|
|
||||||
{
|
{
|
||||||
switch (format)
|
case PixelFormat::BC1_Typeless:
|
||||||
{
|
case PixelFormat::BC2_Typeless:
|
||||||
case PixelFormat::BC1_Typeless:
|
case PixelFormat::BC3_Typeless:
|
||||||
case PixelFormat::BC2_Typeless:
|
case PixelFormat::BC4_Typeless:
|
||||||
case PixelFormat::BC3_Typeless:
|
case PixelFormat::BC5_Typeless:
|
||||||
return PixelFormat::R8G8B8A8_Typeless;
|
case PixelFormat::BC1_UNorm:
|
||||||
case PixelFormat::BC1_UNorm:
|
case PixelFormat::BC2_UNorm:
|
||||||
case PixelFormat::BC2_UNorm:
|
case PixelFormat::BC3_UNorm:
|
||||||
case PixelFormat::BC3_UNorm:
|
case PixelFormat::BC4_UNorm:
|
||||||
return PixelFormat::R8G8B8A8_UNorm;
|
case PixelFormat::BC5_UNorm:
|
||||||
case PixelFormat::BC1_UNorm_sRGB:
|
return GetQualityTextureFormat(false, format);
|
||||||
case PixelFormat::BC2_UNorm_sRGB:
|
case PixelFormat::BC1_UNorm_sRGB:
|
||||||
case PixelFormat::BC3_UNorm_sRGB:
|
case PixelFormat::BC2_UNorm_sRGB:
|
||||||
return PixelFormat::R8G8B8A8_UNorm_sRGB;
|
case PixelFormat::BC3_UNorm_sRGB:
|
||||||
case PixelFormat::BC4_Typeless:
|
case PixelFormat::BC7_UNorm_sRGB:
|
||||||
return PixelFormat::R8_Typeless;
|
return GetQualityTextureFormat(true, format);
|
||||||
case PixelFormat::BC4_UNorm:
|
case PixelFormat::BC4_SNorm:
|
||||||
return PixelFormat::R8_UNorm;
|
return PixelFormat::R8_SNorm;
|
||||||
case PixelFormat::BC4_SNorm:
|
case PixelFormat::BC5_SNorm:
|
||||||
return PixelFormat::R8_SNorm;
|
return PixelFormat::R16G16_SNorm;
|
||||||
case PixelFormat::BC5_Typeless:
|
case PixelFormat::BC6H_Typeless:
|
||||||
return PixelFormat::R16G16_Typeless;
|
case PixelFormat::BC6H_Uf16:
|
||||||
case PixelFormat::BC5_UNorm:
|
case PixelFormat::BC6H_Sf16:
|
||||||
return PixelFormat::R16G16_UNorm;
|
case PixelFormat::BC7_Typeless:
|
||||||
case PixelFormat::BC5_SNorm:
|
case PixelFormat::BC7_UNorm:
|
||||||
return PixelFormat::R16G16_SNorm;
|
return PixelFormat::R16G16B16A16_Float; // TODO: ASTC HDR
|
||||||
case PixelFormat::BC7_Typeless:
|
default:
|
||||||
case PixelFormat::BC6H_Typeless:
|
return format;
|
||||||
return PixelFormat::R16G16B16A16_Typeless;
|
|
||||||
case PixelFormat::BC7_UNorm:
|
|
||||||
case PixelFormat::BC6H_Uf16:
|
|
||||||
case PixelFormat::BC6H_Sf16:
|
|
||||||
return PixelFormat::R16G16B16A16_Float;
|
|
||||||
case PixelFormat::BC7_UNorm_sRGB:
|
|
||||||
return PixelFormat::R16G16B16A16_UNorm;
|
|
||||||
default:
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +152,32 @@ bool iOSPlatformTools::IsNativeCodeFile(CookingData& data, const String& file)
|
|||||||
return extension.IsEmpty() || extension == TEXT("dylib");
|
return extension.IsEmpty() || extension == TEXT("dylib");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void iOSPlatformTools::LoadCache(CookingData& data, IBuildCache* cache, const Span<byte>& bytes)
|
||||||
|
{
|
||||||
|
const auto platformSettings = iOSPlatformSettings::Get();
|
||||||
|
bool invalidTextures = true;
|
||||||
|
if (bytes.Length() == sizeof(iOSPlatformCache))
|
||||||
|
{
|
||||||
|
auto* platformCache = (iOSPlatformCache*)bytes.Get();
|
||||||
|
invalidTextures = platformCache->TexturesQuality != platformSettings->TexturesQuality;
|
||||||
|
}
|
||||||
|
if (invalidTextures)
|
||||||
|
{
|
||||||
|
LOG(Info, "{0} option has been modified.", TEXT("TexturesQuality"));
|
||||||
|
cache->InvalidateCacheTextures();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<byte> iOSPlatformTools::SaveCache(CookingData& data, IBuildCache* cache)
|
||||||
|
{
|
||||||
|
const auto platformSettings = iOSPlatformSettings::Get();
|
||||||
|
iOSPlatformCache platformCache;
|
||||||
|
platformCache.TexturesQuality = platformSettings->TexturesQuality;
|
||||||
|
Array<byte> result;
|
||||||
|
result.Add((const byte*)&platformCache, sizeof(platformCache));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void iOSPlatformTools::OnBuildStarted(CookingData& data)
|
void iOSPlatformTools::OnBuildStarted(CookingData& data)
|
||||||
{
|
{
|
||||||
// Adjust the cooking output folders for packaging app
|
// Adjust the cooking output folders for packaging app
|
||||||
@@ -167,13 +202,25 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data)
|
|||||||
if (EditorUtilities::FormatAppPackageName(appIdentifier))
|
if (EditorUtilities::FormatAppPackageName(appIdentifier))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Copy fresh Gradle project template
|
// Copy fresh XCode project template
|
||||||
if (FileSystem::CopyDirectory(data.OriginalOutputPath, platformDataPath / TEXT("Project"), true))
|
if (FileSystem::CopyDirectory(data.OriginalOutputPath, platformDataPath / TEXT("Project"), true))
|
||||||
{
|
{
|
||||||
LOG(Error, "Failed to deploy XCode project to {0} from {1}", data.OriginalOutputPath, platformDataPath);
|
LOG(Error, "Failed to deploy XCode project to {0} from {1}", data.OriginalOutputPath, platformDataPath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix MoltenVK lib (copied from VulkanSDK xcframework)
|
||||||
|
FileSystem::MoveFile(data.DataOutputPath / TEXT("libMoltenVK.dylib"), data.DataOutputPath / TEXT("MoltenVK"), true);
|
||||||
|
{
|
||||||
|
// Fix rpath to point into dynamic library (rather than framework location)
|
||||||
|
CreateProcessSettings procSettings;
|
||||||
|
procSettings.HiddenWindow = true;
|
||||||
|
procSettings.WorkingDirectory = data.DataOutputPath;
|
||||||
|
procSettings.FileName = TEXT("/usr/bin/install_name_tool");
|
||||||
|
procSettings.Arguments = TEXT("-id \"@rpath/libMoltenVK.dylib\" libMoltenVK.dylib");
|
||||||
|
Platform::CreateProcess(procSettings);
|
||||||
|
}
|
||||||
|
|
||||||
// Format project template files
|
// Format project template files
|
||||||
Dictionary<String, String> configReplaceMap;
|
Dictionary<String, String> configReplaceMap;
|
||||||
configReplaceMap[TEXT("${AppName}")] = appName;
|
configReplaceMap[TEXT("${AppName}")] = appName;
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ public:
|
|||||||
ArchitectureType GetArchitecture() const override;
|
ArchitectureType GetArchitecture() const override;
|
||||||
DotNetAOTModes UseAOT() const override;
|
DotNetAOTModes UseAOT() const override;
|
||||||
PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override;
|
PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override;
|
||||||
|
void LoadCache(CookingData& data, IBuildCache* cache, const Span<byte>& bytes) override;
|
||||||
|
Array<byte> SaveCache(CookingData& data, IBuildCache* cache) override;
|
||||||
bool IsNativeCodeFile(CookingData& data, const String& file) override;
|
bool IsNativeCodeFile(CookingData& data, const String& file) override;
|
||||||
void OnBuildStarted(CookingData& data) override;
|
void OnBuildStarted(CookingData& data) override;
|
||||||
bool OnPostProcess(CookingData& data) override;
|
bool OnPostProcess(CookingData& data) override;
|
||||||
|
|||||||
@@ -8,6 +8,37 @@
|
|||||||
|
|
||||||
class TextureBase;
|
class TextureBase;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The game cooker cache interface.
|
||||||
|
/// </summary>
|
||||||
|
class FLAXENGINE_API IBuildCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all cached entries for assets that contain a given asset type. This forces rebuild for them.
|
||||||
|
/// </summary>
|
||||||
|
virtual void InvalidateCachePerType(const StringView& typeName) = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all cached entries for assets that contain a given asset type. This forces rebuild for them.
|
||||||
|
/// </summary>
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE void InvalidateCachePerType()
|
||||||
|
{
|
||||||
|
InvalidateCachePerType(T::TypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all cached entries for assets that contain a shader. This forces rebuild for them.
|
||||||
|
/// </summary>
|
||||||
|
void InvalidateCacheShaders();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all cached entries for assets that contain a texture. This forces rebuild for them.
|
||||||
|
/// </summary>
|
||||||
|
void InvalidateCacheTextures();
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The platform support tools base interface.
|
/// The platform support tools base interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -76,6 +107,27 @@ public:
|
|||||||
virtual bool IsNativeCodeFile(CookingData& data, const String& file);
|
virtual bool IsNativeCodeFile(CookingData& data, const String& file);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the build cache. Allows to invalidate any cached asset types based on the build settings for incremental builds (eg. invalidate textures/shaders).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The cooking data.</param>
|
||||||
|
/// <param name="data">The build cache interface.</param>
|
||||||
|
/// <param name="data">The loaded cache data. Can be empty when starting a fresh build.</param>
|
||||||
|
virtual void LoadCache(CookingData& data, IBuildCache* cache, const Span<byte>& bytes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the build cache. Allows to store any build settings to be used for cache invalidation on incremental builds.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The cooking data.</param>
|
||||||
|
/// <param name="data">The build cache interface.</param>
|
||||||
|
/// <returns>Data to cache, will be restored during next incremental build.<returns>
|
||||||
|
virtual Array<byte> SaveCache(CookingData& data, IBuildCache* cache)
|
||||||
|
{
|
||||||
|
return Array<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when game building starts.
|
/// Called when game building starts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -31,10 +31,12 @@
|
|||||||
#include "Engine/Graphics/Shaders/GPUShader.h"
|
#include "Engine/Graphics/Shaders/GPUShader.h"
|
||||||
#include "Engine/Graphics/Textures/TextureData.h"
|
#include "Engine/Graphics/Textures/TextureData.h"
|
||||||
#include "Engine/Graphics/Materials/MaterialShader.h"
|
#include "Engine/Graphics/Materials/MaterialShader.h"
|
||||||
|
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||||
#include "Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h"
|
#include "Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h"
|
||||||
#include "Engine/Engine/Base/GameBase.h"
|
#include "Engine/Engine/Base/GameBase.h"
|
||||||
#include "Engine/Engine/Globals.h"
|
#include "Engine/Engine/Globals.h"
|
||||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||||
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
#include "Engine/Scripting/Enums.h"
|
#include "Engine/Scripting/Enums.h"
|
||||||
#if PLATFORM_TOOLS_WINDOWS
|
#if PLATFORM_TOOLS_WINDOWS
|
||||||
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
|
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
|
||||||
@@ -49,6 +51,20 @@
|
|||||||
|
|
||||||
Dictionary<String, CookAssetsStep::ProcessAssetFunc> CookAssetsStep::AssetProcessors;
|
Dictionary<String, CookAssetsStep::ProcessAssetFunc> CookAssetsStep::AssetProcessors;
|
||||||
|
|
||||||
|
void IBuildCache::InvalidateCacheShaders()
|
||||||
|
{
|
||||||
|
InvalidateCachePerType<Shader>();
|
||||||
|
InvalidateCachePerType<Material>();
|
||||||
|
InvalidateCachePerType<ParticleEmitter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IBuildCache::InvalidateCacheTextures()
|
||||||
|
{
|
||||||
|
InvalidateCachePerType<Texture>();
|
||||||
|
InvalidateCachePerType<CubeTexture>();
|
||||||
|
InvalidateCachePerType<SpriteAtlas>();
|
||||||
|
}
|
||||||
|
|
||||||
bool CookAssetsStep::CacheEntry::IsValid(bool withDependencies)
|
bool CookAssetsStep::CacheEntry::IsValid(bool withDependencies)
|
||||||
{
|
{
|
||||||
AssetInfo assetInfo;
|
AssetInfo assetInfo;
|
||||||
@@ -113,15 +129,13 @@ void CookAssetsStep::CacheData::InvalidateCachePerType(const StringView& typeNam
|
|||||||
|
|
||||||
void CookAssetsStep::CacheData::Load(CookingData& data)
|
void CookAssetsStep::CacheData::Load(CookingData& data)
|
||||||
{
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
HeaderFilePath = data.CacheDirectory / String::Format(TEXT("CookedHeader_{0}.bin"), FLAXENGINE_VERSION_BUILD);
|
HeaderFilePath = data.CacheDirectory / String::Format(TEXT("CookedHeader_{0}.bin"), FLAXENGINE_VERSION_BUILD);
|
||||||
CacheFolder = data.CacheDirectory / TEXT("Cooked");
|
CacheFolder = data.CacheDirectory / TEXT("Cooked");
|
||||||
Entries.Clear();
|
Entries.Clear();
|
||||||
|
|
||||||
if (!FileSystem::DirectoryExists(CacheFolder))
|
if (!FileSystem::DirectoryExists(CacheFolder))
|
||||||
{
|
|
||||||
FileSystem::CreateDirectory(CacheFolder);
|
FileSystem::CreateDirectory(CacheFolder);
|
||||||
}
|
|
||||||
|
|
||||||
if (!FileSystem::FileExists(HeaderFilePath))
|
if (!FileSystem::FileExists(HeaderFilePath))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Missing incremental build cooking assets cache.");
|
LOG(Warning, "Missing incremental build cooking assets cache.");
|
||||||
@@ -143,9 +157,7 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
LOG(Info, "Loading incremental build cooking cache (entries count: {0})", entriesCount);
|
LOG(Info, "Loading incremental build cooking cache (entries count: {0})", entriesCount);
|
||||||
|
|
||||||
file->ReadBytes(&Settings, sizeof(Settings));
|
file->ReadBytes(&Settings, sizeof(Settings));
|
||||||
|
|
||||||
Entries.EnsureCapacity(Math::RoundUpToPowerOf2(static_cast<int32>(entriesCount * 3.0f)));
|
Entries.EnsureCapacity(Math::RoundUpToPowerOf2(static_cast<int32>(entriesCount * 3.0f)));
|
||||||
|
|
||||||
Array<Pair<String, DateTime>> fileDependencies;
|
Array<Pair<String, DateTime>> fileDependencies;
|
||||||
@@ -179,6 +191,9 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
|
|||||||
e.FileDependencies = fileDependencies;
|
e.FileDependencies = fileDependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Array<byte> platformCache;
|
||||||
|
file->Read(platformCache);
|
||||||
|
|
||||||
int32 checkChar;
|
int32 checkChar;
|
||||||
file->ReadInt32(&checkChar);
|
file->ReadInt32(&checkChar);
|
||||||
if (checkChar != 13)
|
if (checkChar != 13)
|
||||||
@@ -187,6 +202,9 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
|
|||||||
Entries.Clear();
|
Entries.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per-platform custom data loading (eg. to invalidate textures/shaders options)
|
||||||
|
data.Tools->LoadCache(data, this, ToSpan(platformCache));
|
||||||
|
|
||||||
const auto buildSettings = BuildSettings::Get();
|
const auto buildSettings = BuildSettings::Get();
|
||||||
const auto gameSettings = GameSettings::Get();
|
const auto gameSettings = GameSettings::Get();
|
||||||
|
|
||||||
@@ -200,12 +218,12 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
|
|||||||
if (MATERIAL_GRAPH_VERSION != Settings.Global.MaterialGraphVersion)
|
if (MATERIAL_GRAPH_VERSION != Settings.Global.MaterialGraphVersion)
|
||||||
{
|
{
|
||||||
LOG(Info, "{0} option has been modified.", TEXT("MaterialGraphVersion"));
|
LOG(Info, "{0} option has been modified.", TEXT("MaterialGraphVersion"));
|
||||||
InvalidateCachePerType(Material::TypeName);
|
InvalidateCachePerType<Material>();
|
||||||
}
|
}
|
||||||
if (PARTICLE_GPU_GRAPH_VERSION != Settings.Global.ParticleGraphVersion)
|
if (PARTICLE_GPU_GRAPH_VERSION != Settings.Global.ParticleGraphVersion)
|
||||||
{
|
{
|
||||||
LOG(Info, "{0} option has been modified.", TEXT("ParticleGraphVersion"));
|
LOG(Info, "{0} option has been modified.", TEXT("ParticleGraphVersion"));
|
||||||
InvalidateCachePerType(ParticleEmitter::TypeName);
|
InvalidateCachePerType<ParticleEmitter>();
|
||||||
}
|
}
|
||||||
if (buildSettings->ShadersNoOptimize != Settings.Global.ShadersNoOptimize)
|
if (buildSettings->ShadersNoOptimize != Settings.Global.ShadersNoOptimize)
|
||||||
{
|
{
|
||||||
@@ -262,24 +280,24 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
|
|||||||
#endif
|
#endif
|
||||||
if (invalidateShaders)
|
if (invalidateShaders)
|
||||||
{
|
{
|
||||||
InvalidateCachePerType(Shader::TypeName);
|
InvalidateCachePerType<Shader>();
|
||||||
InvalidateCachePerType(Material::TypeName);
|
InvalidateCachePerType<Material>();
|
||||||
InvalidateCachePerType(ParticleEmitter::TypeName);
|
InvalidateCachePerType<ParticleEmitter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate textures if streaming settings gets modified
|
// Invalidate textures if streaming settings gets modified
|
||||||
if (Settings.Global.StreamingSettingsAssetId != gameSettings->Streaming || (Entries.ContainsKey(gameSettings->Streaming) && !Entries[gameSettings->Streaming].IsValid()))
|
if (Settings.Global.StreamingSettingsAssetId != gameSettings->Streaming || (Entries.ContainsKey(gameSettings->Streaming) && !Entries[gameSettings->Streaming].IsValid()))
|
||||||
{
|
{
|
||||||
InvalidateCachePerType(Texture::TypeName);
|
InvalidateCachePerType<Texture>();
|
||||||
InvalidateCachePerType(CubeTexture::TypeName);
|
InvalidateCachePerType<CubeTexture>();
|
||||||
InvalidateCachePerType(SpriteAtlas::TypeName);
|
InvalidateCachePerType<SpriteAtlas>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CookAssetsStep::CacheData::Save()
|
void CookAssetsStep::CacheData::Save(CookingData& data)
|
||||||
{
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
LOG(Info, "Saving incremental build cooking cache (entries count: {0})", Entries.Count());
|
LOG(Info, "Saving incremental build cooking cache (entries count: {0})", Entries.Count());
|
||||||
|
|
||||||
auto file = FileWriteStream::Open(HeaderFilePath);
|
auto file = FileWriteStream::Open(HeaderFilePath);
|
||||||
if (file == nullptr)
|
if (file == nullptr)
|
||||||
return;
|
return;
|
||||||
@@ -302,6 +320,7 @@ void CookAssetsStep::CacheData::Save()
|
|||||||
file->Write(f.Second);
|
file->Write(f.Second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file->Write(data.Tools->SaveCache(data, this));
|
||||||
file->WriteInt32(13);
|
file->WriteInt32(13);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +643,8 @@ bool ProcessTextureBase(CookAssetsStep::AssetCookData& data)
|
|||||||
const auto asset = static_cast<TextureBase*>(data.Asset);
|
const auto asset = static_cast<TextureBase*>(data.Asset);
|
||||||
const auto& assetHeader = asset->StreamingTexture()->GetHeader();
|
const auto& assetHeader = asset->StreamingTexture()->GetHeader();
|
||||||
const auto format = asset->Format();
|
const auto format = asset->Format();
|
||||||
const auto targetFormat = data.Data.Tools->GetTextureFormat(data.Data, asset, format);
|
auto targetFormat = data.Data.Tools->GetTextureFormat(data.Data, asset, format);
|
||||||
|
CHECK_RETURN(!PixelFormatExtensions::IsTypeless(targetFormat), true);
|
||||||
const auto streamingSettings = StreamingSettings::Get();
|
const auto streamingSettings = StreamingSettings::Get();
|
||||||
int32 mipLevelsMax = GPU_MAX_TEXTURE_MIP_LEVELS;
|
int32 mipLevelsMax = GPU_MAX_TEXTURE_MIP_LEVELS;
|
||||||
if (assetHeader->TextureGroup >= 0 && assetHeader->TextureGroup < streamingSettings->TextureGroups.Count())
|
if (assetHeader->TextureGroup >= 0 && assetHeader->TextureGroup < streamingSettings->TextureGroups.Count())
|
||||||
@@ -634,6 +654,11 @@ bool ProcessTextureBase(CookAssetsStep::AssetCookData& data)
|
|||||||
group.MipLevelsMaxPerPlatform.TryGet(data.Data.Tools->GetPlatform(), mipLevelsMax);
|
group.MipLevelsMaxPerPlatform.TryGet(data.Data.Tools->GetPlatform(), mipLevelsMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If texture is smaller than the block size of the target format (eg. 4x4 texture using ASTC_6x6) then fallback to uncompressed
|
||||||
|
int32 blockSize = PixelFormatExtensions::ComputeBlockSize(targetFormat);
|
||||||
|
if (assetHeader->Width < blockSize || assetHeader->Height < blockSize || (blockSize != 1 && mipLevelsMax < 4))
|
||||||
|
targetFormat = PixelFormatExtensions::FindUncompressedFormat(format);
|
||||||
|
|
||||||
// Faster path if don't need to modify texture for the target platform
|
// Faster path if don't need to modify texture for the target platform
|
||||||
if (format == targetFormat && assetHeader->MipLevels <= mipLevelsMax)
|
if (format == targetFormat && assetHeader->MipLevels <= mipLevelsMax)
|
||||||
{
|
{
|
||||||
@@ -961,6 +986,7 @@ public:
|
|||||||
const int32 count = addedEntries.Count();
|
const int32 count = addedEntries.Count();
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return false;
|
return false;
|
||||||
|
PROFILE_CPU();
|
||||||
|
|
||||||
// Get assets init data and load all chunks
|
// Get assets init data and load all chunks
|
||||||
Array<AssetInitData> assetsData;
|
Array<AssetInitData> assetsData;
|
||||||
@@ -1143,7 +1169,7 @@ bool CookAssetsStep::Perform(CookingData& data)
|
|||||||
// Cook asset
|
// Cook asset
|
||||||
if (Process(data, cache, assetRef.Get()))
|
if (Process(data, cache, assetRef.Get()))
|
||||||
{
|
{
|
||||||
cache.Save();
|
cache.Save(data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
data.Stats.CookedAssets++;
|
data.Stats.CookedAssets++;
|
||||||
@@ -1151,12 +1177,12 @@ bool CookAssetsStep::Perform(CookingData& data)
|
|||||||
// Auto save build cache after every few cooked assets (reduces next build time if cooking fails later)
|
// Auto save build cache after every few cooked assets (reduces next build time if cooking fails later)
|
||||||
if (data.Stats.CookedAssets % 50 == 0)
|
if (data.Stats.CookedAssets % 50 == 0)
|
||||||
{
|
{
|
||||||
cache.Save();
|
cache.Save(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save build cache header
|
// Save build cache header
|
||||||
cache.Save();
|
cache.Save(data);
|
||||||
|
|
||||||
// Create build game header
|
// Create build game header
|
||||||
{
|
{
|
||||||
@@ -1173,7 +1199,6 @@ bool CookAssetsStep::Perform(CookingData& data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
stream->WriteInt32(('x' + 'D') * 131); // think about it as '131 times xD'
|
stream->WriteInt32(('x' + 'D') * 131); // think about it as '131 times xD'
|
||||||
|
|
||||||
stream->WriteInt32(FLAXENGINE_VERSION_BUILD);
|
stream->WriteInt32(FLAXENGINE_VERSION_BUILD);
|
||||||
|
|
||||||
Array<byte> bytes;
|
Array<byte> bytes;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Editor/Cooker/GameCooker.h"
|
#include "Editor/Cooker/GameCooker.h"
|
||||||
|
#include "Editor/Cooker/PlatformTools.h"
|
||||||
#include "Engine/Core/Types/Pair.h"
|
#include "Engine/Core/Types/Pair.h"
|
||||||
#include "Engine/Core/Types/DateTime.h"
|
#include "Engine/Core/Types/DateTime.h"
|
||||||
#include "Engine/Core/Collections/Dictionary.h"
|
#include "Engine/Core/Collections/Dictionary.h"
|
||||||
@@ -56,7 +57,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Assets cooking cache data (incremental building feature).
|
/// Assets cooking cache data (incremental building feature).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
struct FLAXENGINE_API CacheData
|
struct FLAXENGINE_API CacheData : public IBuildCache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cache header file path.
|
/// The cache header file path.
|
||||||
@@ -136,16 +137,6 @@ public:
|
|||||||
/// <returns>The added entry reference.</returns>
|
/// <returns>The added entry reference.</returns>
|
||||||
CacheEntry& CreateEntry(const Asset* asset, String& cachedFilePath);
|
CacheEntry& CreateEntry(const Asset* asset, String& cachedFilePath);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all cached entries for assets that contain a shader. This forces rebuild for them.
|
|
||||||
/// </summary>
|
|
||||||
void InvalidateShaders();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all cached entries for assets that contain a texture. This forces rebuild for them.
|
|
||||||
/// </summary>
|
|
||||||
void InvalidateCachePerType(const StringView& typeName);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the cache for the given cooking data.
|
/// Loads the cache for the given cooking data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -155,7 +146,11 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves this cache (header file).
|
/// Saves this cache (header file).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Save();
|
/// <param name="data">The data.</param>
|
||||||
|
void Save(CookingData& data);
|
||||||
|
|
||||||
|
using IBuildCache::InvalidateCachePerType;
|
||||||
|
void InvalidateCachePerType(const StringView& typeName) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FLAXENGINE_API AssetCookData
|
struct FLAXENGINE_API AssetCookData
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
for (String& version : versions)
|
for (String& version : versions)
|
||||||
{
|
{
|
||||||
version = String(StringUtils::GetFileName(version));
|
version = String(StringUtils::GetFileName(version));
|
||||||
if (!version.StartsWith(TEXT("7.")) && !version.StartsWith(TEXT("8."))) // .NET 7 or .NET 8
|
if (!version.StartsWith(TEXT("8."))) // Check for major part of 8.0
|
||||||
version.Clear();
|
version.Clear();
|
||||||
}
|
}
|
||||||
Sorting::QuickSort(versions);
|
Sorting::QuickSort(versions);
|
||||||
@@ -204,14 +204,14 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
{
|
{
|
||||||
// AOT runtime files inside Engine Platform folder
|
// AOT runtime files inside Engine Platform folder
|
||||||
packFolder /= TEXT("Dotnet");
|
packFolder /= TEXT("Dotnet");
|
||||||
dstDotnetLibs /= TEXT("lib/net7.0");
|
dstDotnetLibs /= TEXT("lib/net8.0");
|
||||||
srcDotnetLibs = packFolder / TEXT("lib/net7.0");
|
srcDotnetLibs = packFolder / TEXT("lib/net8.0");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Runtime files inside Dotnet SDK folder but placed for AOT
|
// Runtime files inside Dotnet SDK folder but placed for AOT
|
||||||
dstDotnetLibs /= TEXT("lib/net7.0");
|
dstDotnetLibs /= TEXT("lib/net8.0");
|
||||||
srcDotnetLibs /= TEXT("../lib/net7.0");
|
srcDotnetLibs /= TEXT("../lib/net8.0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -219,16 +219,18 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
if (srcDotnetFromEngine)
|
if (srcDotnetFromEngine)
|
||||||
{
|
{
|
||||||
// Runtime files inside Engine Platform folder
|
// Runtime files inside Engine Platform folder
|
||||||
dstDotnetLibs /= TEXT("lib/net7.0");
|
dstDotnetLibs /= TEXT("lib/net8.0");
|
||||||
srcDotnetLibs /= TEXT("lib/net7.0");
|
srcDotnetLibs /= TEXT("lib/net8.0");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Runtime files inside Dotnet SDK folder
|
// Runtime files inside Dotnet SDK folder
|
||||||
dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App");
|
dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App");
|
||||||
srcDotnetLibs /= TEXT("../lib/net7.0");
|
srcDotnetLibs /= TEXT("../lib/net8.0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LOG(Info, "Copying .NET files from {} to {}", packFolder, dstDotnet);
|
||||||
|
LOG(Info, "Copying .NET files from {} to {}", srcDotnetLibs, dstDotnetLibs);
|
||||||
FileSystem::CopyFile(dstDotnet / TEXT("LICENSE.TXT"), packFolder / TEXT("LICENSE.txt"));
|
FileSystem::CopyFile(dstDotnet / TEXT("LICENSE.TXT"), packFolder / TEXT("LICENSE.txt"));
|
||||||
FileSystem::CopyFile(dstDotnet / TEXT("LICENSE.TXT"), packFolder / TEXT("LICENSE.TXT"));
|
FileSystem::CopyFile(dstDotnet / TEXT("LICENSE.TXT"), packFolder / TEXT("LICENSE.TXT"));
|
||||||
FileSystem::CopyFile(dstDotnet / TEXT("THIRD-PARTY-NOTICES.TXT"), packFolder / TEXT("ThirdPartyNotices.txt"));
|
FileSystem::CopyFile(dstDotnet / TEXT("THIRD-PARTY-NOTICES.TXT"), packFolder / TEXT("ThirdPartyNotices.txt"));
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ void PrecompileAssembliesStep::OnBuildStarted(CookingData& data)
|
|||||||
if (cachedData != aotModeCacheValue)
|
if (cachedData != aotModeCacheValue)
|
||||||
{
|
{
|
||||||
LOG(Info, "AOT cache invalidation");
|
LOG(Info, "AOT cache invalidation");
|
||||||
FileSystem::DeleteDirectory(data.ManagedCodeOutputPath);
|
FileSystem::DeleteDirectory(data.ManagedCodeOutputPath); // Remove AOT cache
|
||||||
|
FileSystem::DeleteDirectory(data.DataOutputPath / TEXT("Dotnet")); // Remove deployed Dotnet libs (be sure to remove any leftovers from previous build)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!FileSystem::DirectoryExists(data.ManagedCodeOutputPath))
|
if (!FileSystem::DirectoryExists(data.ManagedCodeOutputPath))
|
||||||
|
|||||||
@@ -385,6 +385,15 @@ namespace FlaxEditor.CustomEditors
|
|||||||
LinkedLabel = label;
|
LinkedLabel = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal bool CanEditValue
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var readOnly = Values.Info.GetAttribute<ReadOnlyAttribute>();
|
||||||
|
return readOnly == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, the value reverting to default/reference will be handled via iteration over children editors, instead of for a whole object at once.
|
/// If true, the value reverting to default/reference will be handled via iteration over children editors, instead of for a whole object at once.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -413,7 +422,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
{
|
{
|
||||||
if (!Values.IsDefaultValueModified)
|
if (!Values.IsDefaultValueModified)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return CanEditValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +431,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void RevertToDefaultValue()
|
public void RevertToDefaultValue()
|
||||||
{
|
{
|
||||||
if (!Values.HasDefaultValue)
|
if (!Values.HasDefaultValue || !CanEditValue)
|
||||||
return;
|
return;
|
||||||
RevertDiffToDefault();
|
RevertDiffToDefault();
|
||||||
}
|
}
|
||||||
@@ -471,7 +480,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Values.IsReferenceValueModified)
|
if (CanRevertReferenceValue)
|
||||||
SetValueToReference();
|
SetValueToReference();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,7 +494,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
{
|
{
|
||||||
if (!Values.IsReferenceValueModified)
|
if (!Values.IsReferenceValueModified)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return CanEditValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,7 +503,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void RevertToReferenceValue()
|
public void RevertToReferenceValue()
|
||||||
{
|
{
|
||||||
if (!Values.HasReferenceValue)
|
if (!Values.HasReferenceValue || !CanEditValue)
|
||||||
return;
|
return;
|
||||||
RevertDiffToReference();
|
RevertDiffToReference();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
#include "Engine/Core/Types/DateTime.h"
|
#include "Engine/Core/Types/DateTime.h"
|
||||||
#include "Engine/Core/Types/TimeSpan.h"
|
#include "Engine/Core/Types/TimeSpan.h"
|
||||||
|
#include "Engine/Core/Types/Stopwatch.h"
|
||||||
#include "Engine/Core/Collections/Dictionary.h"
|
#include "Engine/Core/Collections/Dictionary.h"
|
||||||
#include "Engine/Engine/EngineService.h"
|
#include "Engine/Engine/EngineService.h"
|
||||||
#include "Engine/Scripting/Scripting.h"
|
#include "Engine/Scripting/Scripting.h"
|
||||||
@@ -81,7 +82,7 @@ bool CustomEditorsUtilService::Init()
|
|||||||
|
|
||||||
void OnAssemblyLoaded(MAssembly* assembly)
|
void OnAssemblyLoaded(MAssembly* assembly)
|
||||||
{
|
{
|
||||||
const auto startTime = DateTime::NowUTC();
|
Stopwatch stopwatch;
|
||||||
|
|
||||||
// Prepare FlaxEngine
|
// Prepare FlaxEngine
|
||||||
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||||
@@ -162,8 +163,8 @@ void OnAssemblyLoaded(MAssembly* assembly)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto endTime = DateTime::NowUTC();
|
stopwatch.Stop();
|
||||||
LOG(Info, "Assembly \'{0}\' scanned for custom editors in {1} ms", assembly->ToString(), (int32)(endTime - startTime).GetTotalMilliseconds());
|
LOG(Info, "Assembly \'{0}\' scanned for custom editors in {1} ms", assembly->ToString(), stopwatch.GetMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnAssemblyUnloading(MAssembly* assembly)
|
void OnAssemblyUnloading(MAssembly* assembly)
|
||||||
|
|||||||
@@ -589,25 +589,27 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
LayoutElementsContainer yEl;
|
LayoutElementsContainer yEl;
|
||||||
LayoutElementsContainer hEl;
|
LayoutElementsContainer hEl;
|
||||||
LayoutElementsContainer vEl;
|
LayoutElementsContainer vEl;
|
||||||
|
Color axisColorX = ActorTransformEditor.AxisColorX;
|
||||||
|
Color axisColorY = ActorTransformEditor.AxisColorY;
|
||||||
if (xEq)
|
if (xEq)
|
||||||
{
|
{
|
||||||
xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values));
|
xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX);
|
||||||
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values));
|
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values));
|
xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX);
|
||||||
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values));
|
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX);
|
||||||
}
|
}
|
||||||
if (yEq)
|
if (yEq)
|
||||||
{
|
{
|
||||||
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values));
|
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY);
|
||||||
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values));
|
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values));
|
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY);
|
||||||
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values));
|
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY);
|
||||||
}
|
}
|
||||||
xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y);
|
xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y);
|
||||||
xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y);
|
xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y);
|
||||||
@@ -624,28 +626,34 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
|
|
||||||
private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont)
|
private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont)
|
||||||
{
|
{
|
||||||
var horUp = cont.VerticalPanel();
|
var panel = cont.VerticalPanel();
|
||||||
horUp.Panel.Margin = Margin.Zero;
|
panel.Panel.Margin = Margin.Zero;
|
||||||
return horUp;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CustomElementsContainer<UniformGridPanel> UniformGridTwoByOne(LayoutElementsContainer cont)
|
private CustomElementsContainer<UniformGridPanel> UniformGridTwoByOne(LayoutElementsContainer cont)
|
||||||
{
|
{
|
||||||
var horUp = cont.CustomContainer<UniformGridPanel>();
|
var grid = cont.CustomContainer<UniformGridPanel>();
|
||||||
horUp.CustomControl.SlotsHorizontally = 2;
|
grid.CustomControl.SlotsHorizontally = 2;
|
||||||
horUp.CustomControl.SlotsVertically = 1;
|
grid.CustomControl.SlotsVertically = 1;
|
||||||
horUp.CustomControl.SlotPadding = Margin.Zero;
|
grid.CustomControl.SlotPadding = Margin.Zero;
|
||||||
horUp.CustomControl.ClipChildren = false;
|
grid.CustomControl.ClipChildren = false;
|
||||||
return horUp;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values)
|
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor)
|
||||||
{
|
{
|
||||||
CustomElementsContainer<UniformGridPanel> hor = UniformGridTwoByOne(el);
|
var grid = UniformGridTwoByOne(el);
|
||||||
hor.CustomControl.SlotPadding = new Margin(5, 5, 0, 0);
|
grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1);
|
||||||
LabelElement lab = hor.Label(text);
|
var label = grid.Label(text);
|
||||||
hor.Object(values);
|
var editor = grid.Object(values);
|
||||||
return hor;
|
if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement)
|
||||||
|
{
|
||||||
|
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||||
|
floatEditorElement.ValueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor);
|
||||||
|
floatEditorElement.ValueBox.BorderSelectedColor = borderColor;
|
||||||
|
}
|
||||||
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _cachedXEq;
|
private bool _cachedXEq;
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f);
|
public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The axes colors grey out scale when input field is not focused.
|
||||||
|
/// </summary>
|
||||||
|
public static float AxisGreyOutFactor = 0.6f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom editor for actor position property.
|
/// Custom editor for actor position property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,13 +44,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
// Override colors
|
// Override colors
|
||||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||||
var grayOutFactor = 0.6f;
|
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
|
||||||
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
|
|
||||||
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
||||||
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
|
XElement.ValueBox.Category = Utils.ValueCategory.Distance;
|
||||||
|
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
|
||||||
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
||||||
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
|
YElement.ValueBox.Category = Utils.ValueCategory.Distance;
|
||||||
|
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
|
||||||
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
||||||
|
ZElement.ValueBox.Category = Utils.ValueCategory.Distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,13 +69,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
// Override colors
|
// Override colors
|
||||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||||
var grayOutFactor = 0.6f;
|
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
|
||||||
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
|
|
||||||
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
XElement.ValueBox.BorderSelectedColor = AxisColorX;
|
||||||
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
|
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
|
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
|
||||||
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
YElement.ValueBox.BorderSelectedColor = AxisColorY;
|
||||||
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
|
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
|
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
|
||||||
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
|
||||||
|
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,14 +111,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
SetLinkStyle();
|
SetLinkStyle();
|
||||||
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value);
|
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value);
|
||||||
_linkButton.LocalX += textSize.X + 10;
|
_linkButton.LocalX += textSize.X + 10;
|
||||||
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
if (LinkedLabel != null)
|
||||||
{
|
{
|
||||||
menu.AddSeparator();
|
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||||
if (LinkValues)
|
{
|
||||||
menu.AddButton("Unlink", ToggleLink).LinkTooltip("Unlinks scale components from uniform scaling");
|
menu.AddSeparator();
|
||||||
else
|
if (LinkValues)
|
||||||
menu.AddButton("Link", ToggleLink).LinkTooltip("Links scale components for uniform scaling");
|
menu.AddButton("Unlink", ToggleLink).LinkTooltip("Unlinks scale components from uniform scaling");
|
||||||
};
|
else
|
||||||
|
menu.AddButton("Link", ToggleLink).LinkTooltip("Links scale components for uniform scaling");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Override colors
|
// Override colors
|
||||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||||
|
|||||||
@@ -21,32 +21,31 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
{
|
{
|
||||||
_element = null;
|
var doubleValue = layout.DoubleValue();
|
||||||
|
doubleValue.ValueBox.ValueChanged += OnValueChanged;
|
||||||
// Try get limit attribute for value min/max range setting and slider speed
|
doubleValue.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
_element = doubleValue;
|
||||||
var attributes = Values.GetAttributes();
|
var attributes = Values.GetAttributes();
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
|
var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||||
if (limit != null)
|
doubleValue.SetLimits(limit);
|
||||||
|
var valueCategory = ((ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute))?.Category ?? Utils.ValueCategory.None;
|
||||||
|
if (valueCategory != Utils.ValueCategory.None)
|
||||||
{
|
{
|
||||||
// Use double value editor with limit
|
doubleValue.SetCategory(valueCategory);
|
||||||
var doubleValue = layout.DoubleValue();
|
if (LinkedLabel != null)
|
||||||
doubleValue.SetLimits((LimitAttribute)limit);
|
{
|
||||||
doubleValue.ValueBox.ValueChanged += OnValueChanged;
|
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||||
doubleValue.ValueBox.SlidingEnd += ClearToken;
|
{
|
||||||
_element = doubleValue;
|
menu.AddSeparator();
|
||||||
return;
|
var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); });
|
||||||
|
mb.AutoCheck = true;
|
||||||
|
mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_element == null)
|
|
||||||
{
|
|
||||||
// Use double value editor
|
|
||||||
var doubleValue = layout.DoubleValue();
|
|
||||||
doubleValue.ValueBox.ValueChanged += OnValueChanged;
|
|
||||||
doubleValue.ValueBox.SlidingEnd += ClearToken;
|
|
||||||
_element = doubleValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnValueChanged()
|
private void OnValueChanged()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.CustomEditors.Elements;
|
using FlaxEditor.CustomEditors.Elements;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using Utils = FlaxEngine.Utils;
|
||||||
|
|
||||||
namespace FlaxEditor.CustomEditors.Editors
|
namespace FlaxEditor.CustomEditors.Editors
|
||||||
{
|
{
|
||||||
@@ -27,41 +28,42 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
{
|
{
|
||||||
_element = null;
|
_element = null;
|
||||||
|
|
||||||
// Try get limit attribute for value min/max range setting and slider speed
|
|
||||||
var attributes = Values.GetAttributes();
|
var attributes = Values.GetAttributes();
|
||||||
|
var range = (RangeAttribute)attributes?.FirstOrDefault(x => x is RangeAttribute);
|
||||||
|
if (range != null)
|
||||||
|
{
|
||||||
|
// Use slider
|
||||||
|
var slider = layout.Slider();
|
||||||
|
slider.Slider.SetLimits(range);
|
||||||
|
slider.Slider.ValueChanged += OnValueChanged;
|
||||||
|
slider.Slider.SlidingEnd += ClearToken;
|
||||||
|
_element = slider;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var floatValue = layout.FloatValue();
|
||||||
|
floatValue.ValueBox.ValueChanged += OnValueChanged;
|
||||||
|
floatValue.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
_element = floatValue;
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
var range = attributes.FirstOrDefault(x => x is RangeAttribute);
|
var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||||
if (range != null)
|
floatValue.SetLimits(limit);
|
||||||
|
var valueCategory = ((ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute))?.Category ?? Utils.ValueCategory.None;
|
||||||
|
if (valueCategory != Utils.ValueCategory.None)
|
||||||
{
|
{
|
||||||
// Use slider
|
floatValue.SetCategory(valueCategory);
|
||||||
var slider = layout.Slider();
|
if (LinkedLabel != null)
|
||||||
slider.SetLimits((RangeAttribute)range);
|
{
|
||||||
slider.Slider.ValueChanged += OnValueChanged;
|
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||||
slider.Slider.SlidingEnd += ClearToken;
|
{
|
||||||
_element = slider;
|
menu.AddSeparator();
|
||||||
return;
|
var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); });
|
||||||
|
mb.AutoCheck = true;
|
||||||
|
mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
|
|
||||||
if (limit != null)
|
|
||||||
{
|
|
||||||
// Use float value editor with limit
|
|
||||||
var floatValue = layout.FloatValue();
|
|
||||||
floatValue.SetLimits((LimitAttribute)limit);
|
|
||||||
floatValue.ValueBox.ValueChanged += OnValueChanged;
|
|
||||||
floatValue.ValueBox.SlidingEnd += ClearToken;
|
|
||||||
_element = floatValue;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_element == null)
|
|
||||||
{
|
|
||||||
// Use float value editor
|
|
||||||
var floatValue = layout.FloatValue();
|
|
||||||
floatValue.ValueBox.ValueChanged += OnValueChanged;
|
|
||||||
floatValue.ValueBox.SlidingEnd += ClearToken;
|
|
||||||
_element = floatValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
{
|
{
|
||||||
LinkedLabel.SetupContextMenu += OnSetupContextMenu;
|
if (LinkedLabel != null)
|
||||||
|
LinkedLabel.SetupContextMenu += OnSetupContextMenu;
|
||||||
var comboBoxElement = layout.ComboBox();
|
var comboBoxElement = layout.ComboBox();
|
||||||
_comboBox = comboBoxElement.ComboBox;
|
_comboBox = comboBoxElement.ComboBox;
|
||||||
var names = new List<string>();
|
var names = new List<string>();
|
||||||
|
|||||||
@@ -45,23 +45,29 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
gridControl.SlotsVertically = 1;
|
gridControl.SlotsVertically = 1;
|
||||||
|
|
||||||
XElement = grid.FloatValue();
|
XElement = grid.FloatValue();
|
||||||
|
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
XElement.ValueBox.ValueChanged += OnValueChanged;
|
||||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
YElement = grid.FloatValue();
|
YElement = grid.FloatValue();
|
||||||
|
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
YElement.ValueBox.ValueChanged += OnValueChanged;
|
||||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
ZElement = grid.FloatValue();
|
ZElement = grid.FloatValue();
|
||||||
|
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
||||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
if (LinkedLabel != null)
|
||||||
{
|
{
|
||||||
menu.AddSeparator();
|
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||||
var value = ((Quaternion)Values[0]).EulerAngles;
|
{
|
||||||
menu.AddButton("Copy Euler", () => { Clipboard.Text = JsonSerializer.Serialize(value); }).TooltipText = "Copy the Euler Angles in Degrees";
|
menu.AddSeparator();
|
||||||
};
|
var value = ((Quaternion)Values[0]).EulerAngles;
|
||||||
|
menu.AddButton("Copy Euler", () => { Clipboard.Text = JsonSerializer.Serialize(value); }).TooltipText = "Copy the Euler Angles in Degrees";
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnValueChanged()
|
private void OnValueChanged()
|
||||||
|
|||||||
@@ -70,25 +70,48 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
LimitAttribute limit = null;
|
LimitAttribute limit = null;
|
||||||
var attributes = Values.GetAttributes();
|
var attributes = Values.GetAttributes();
|
||||||
|
var category = Utils.ValueCategory.None;
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||||
|
var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute);
|
||||||
|
if (categoryAttribute != null)
|
||||||
|
category = categoryAttribute.Category;
|
||||||
}
|
}
|
||||||
|
|
||||||
XElement = grid.FloatValue();
|
XElement = grid.FloatValue();
|
||||||
XElement.SetLimits(limit);
|
XElement.SetLimits(limit);
|
||||||
|
XElement.SetCategory(category);
|
||||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
YElement = grid.FloatValue();
|
YElement = grid.FloatValue();
|
||||||
YElement.SetLimits(limit);
|
YElement.SetLimits(limit);
|
||||||
|
YElement.SetCategory(category);
|
||||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
ZElement = grid.FloatValue();
|
ZElement = grid.FloatValue();
|
||||||
ZElement.SetLimits(limit);
|
ZElement.SetLimits(limit);
|
||||||
|
ZElement.SetCategory(category);
|
||||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
|
if (LinkedLabel != null)
|
||||||
|
{
|
||||||
|
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||||
|
{
|
||||||
|
menu.AddSeparator();
|
||||||
|
var mb = menu.AddButton("Show formatted", bt =>
|
||||||
|
{
|
||||||
|
XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
|
||||||
|
YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
|
||||||
|
ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
|
||||||
|
});
|
||||||
|
mb.AutoCheck = true;
|
||||||
|
mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnXValueChanged()
|
private void OnXValueChanged()
|
||||||
@@ -248,26 +271,49 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
gridControl.SlotsVertically = 1;
|
gridControl.SlotsVertically = 1;
|
||||||
|
|
||||||
LimitAttribute limit = null;
|
LimitAttribute limit = null;
|
||||||
|
Utils.ValueCategory category = Utils.ValueCategory.None;
|
||||||
var attributes = Values.GetAttributes();
|
var attributes = Values.GetAttributes();
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||||
|
var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute);
|
||||||
|
if (categoryAttribute != null)
|
||||||
|
category = categoryAttribute.Category;
|
||||||
}
|
}
|
||||||
|
|
||||||
XElement = grid.DoubleValue();
|
XElement = grid.DoubleValue();
|
||||||
XElement.SetLimits(limit);
|
XElement.SetLimits(limit);
|
||||||
|
XElement.SetCategory(category);
|
||||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
XElement.ValueBox.ValueChanged += OnValueChanged;
|
||||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
YElement = grid.DoubleValue();
|
YElement = grid.DoubleValue();
|
||||||
YElement.SetLimits(limit);
|
YElement.SetLimits(limit);
|
||||||
|
YElement.SetCategory(category);
|
||||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
YElement.ValueBox.ValueChanged += OnValueChanged;
|
||||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
ZElement = grid.DoubleValue();
|
ZElement = grid.DoubleValue();
|
||||||
ZElement.SetLimits(limit);
|
ZElement.SetLimits(limit);
|
||||||
|
ZElement.SetCategory(category);
|
||||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
||||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||||
|
|
||||||
|
if (LinkedLabel != null)
|
||||||
|
{
|
||||||
|
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||||
|
{
|
||||||
|
menu.AddSeparator();
|
||||||
|
var mb = menu.AddButton("Show formatted", bt =>
|
||||||
|
{
|
||||||
|
XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
|
||||||
|
YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
|
||||||
|
ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
|
||||||
|
});
|
||||||
|
mb.AutoCheck = true;
|
||||||
|
mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnValueChanged()
|
private void OnValueChanged()
|
||||||
|
|||||||
@@ -51,6 +51,15 @@ namespace FlaxEditor.CustomEditors.Elements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the editor value category.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="category">The category.</param>
|
||||||
|
public void SetCategory(Utils.ValueCategory category)
|
||||||
|
{
|
||||||
|
ValueBox.Category = category;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the editor limits from member <see cref="LimitAttribute"/>.
|
/// Sets the editor limits from member <see cref="LimitAttribute"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -51,6 +51,15 @@ namespace FlaxEditor.CustomEditors.Elements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the editor value category.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="category">The category.</param>
|
||||||
|
public void SetCategory(Utils.ValueCategory category)
|
||||||
|
{
|
||||||
|
ValueBox.Category = category;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the editor limits from member <see cref="LimitAttribute"/>.
|
/// Sets the editor limits from member <see cref="LimitAttribute"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -315,9 +315,9 @@ namespace FlaxEditor.CustomEditors
|
|||||||
internal LabelElement Header(HeaderAttribute header)
|
internal LabelElement Header(HeaderAttribute header)
|
||||||
{
|
{
|
||||||
var element = Header(header.Text);
|
var element = Header(header.Text);
|
||||||
if (header.FontSize != -1)
|
if (header.FontSize > 0)
|
||||||
element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize);
|
element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize);
|
||||||
if (header.Color != 0)
|
if (header.Color > 0)
|
||||||
element.Label.TextColor = Color.FromRGBA(header.Color);
|
element.Label.TextColor = Color.FromRGBA(header.Color);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -407,12 +407,26 @@ int32 Editor::LoadProduct()
|
|||||||
{
|
{
|
||||||
Array<String> projectFiles;
|
Array<String> projectFiles;
|
||||||
FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*.flaxproj"), DirectorySearchOption::TopDirectoryOnly);
|
FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*.flaxproj"), DirectorySearchOption::TopDirectoryOnly);
|
||||||
if (projectFiles.Count() == 1)
|
if (projectFiles.Count() > 1)
|
||||||
{
|
{
|
||||||
// Skip creating new project if it already exists
|
Platform::Fatal(TEXT("Too many project files."));
|
||||||
LOG(Info, "Skip creatinng new project because it already exists");
|
return -2;
|
||||||
|
}
|
||||||
|
else if (projectFiles.Count() == 1)
|
||||||
|
{
|
||||||
|
LOG(Info, "Skip creating new project because it already exists");
|
||||||
CommandLine::Options.NewProject.Reset();
|
CommandLine::Options.NewProject.Reset();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Array<String> files;
|
||||||
|
FileSystem::DirectoryGetFiles(files, projectPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
|
||||||
|
if (files.Count() > 0)
|
||||||
|
{
|
||||||
|
Platform::Fatal(String::Format(TEXT("Target project folder '{0}' is not empty."), projectPath));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (CommandLine::Options.NewProject)
|
if (CommandLine::Options.NewProject)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -290,7 +290,6 @@ namespace FlaxEditor
|
|||||||
|
|
||||||
StateMachine = new EditorStateMachine(this);
|
StateMachine = new EditorStateMachine(this);
|
||||||
Undo = new EditorUndo(this);
|
Undo = new EditorUndo(this);
|
||||||
UIControl.FallbackParentGetDelegate += OnUIControlFallbackParentGet;
|
|
||||||
|
|
||||||
if (newProject)
|
if (newProject)
|
||||||
InitProject();
|
InitProject();
|
||||||
@@ -355,27 +354,6 @@ namespace FlaxEditor
|
|||||||
StateMachine.LoadingState.StartInitEnding(skipCompile);
|
StateMachine.LoadingState.StartInitEnding(skipCompile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContainerControl OnUIControlFallbackParentGet(UIControl control)
|
|
||||||
{
|
|
||||||
// Check if prefab root control is this UIControl
|
|
||||||
var loadingPreview = Viewport.Previews.PrefabPreview.LoadingPreview;
|
|
||||||
var activePreviews = Viewport.Previews.PrefabPreview.ActivePreviews;
|
|
||||||
if (activePreviews != null)
|
|
||||||
{
|
|
||||||
foreach (var preview in activePreviews)
|
|
||||||
{
|
|
||||||
if (preview == loadingPreview ||
|
|
||||||
(preview.Instance != null && (preview.Instance == control || preview.Instance.HasActorInHierarchy(control))))
|
|
||||||
{
|
|
||||||
// Link it to the prefab preview to see it in the editor
|
|
||||||
preview.customControlLinked = control;
|
|
||||||
return preview;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RegisterModule(EditorModule module)
|
internal void RegisterModule(EditorModule module)
|
||||||
{
|
{
|
||||||
Log("Register Editor module " + module);
|
Log("Register Editor module " + module);
|
||||||
|
|||||||
@@ -431,7 +431,6 @@ namespace FlaxEditor.GUI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected CurveEditor()
|
protected CurveEditor()
|
||||||
{
|
{
|
||||||
_tickStrengths = new float[TickSteps.Length];
|
|
||||||
Accessor.GetDefaultValue(out DefaultValue);
|
Accessor.GetDefaultValue(out DefaultValue);
|
||||||
|
|
||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
@@ -780,75 +779,31 @@ namespace FlaxEditor.GUI
|
|||||||
return _mainPanel.PointToParent(point);
|
return _mainPanel.PointToParent(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawAxis(Float2 axis, ref Rectangle viewRect, float min, float max, float pixelRange)
|
private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange)
|
||||||
{
|
{
|
||||||
int minDistanceBetweenTicks = 20;
|
Utilities.Utils.DrawCurveTicks((float tick, float strength) =>
|
||||||
int maxDistanceBetweenTicks = 60;
|
|
||||||
var range = max - min;
|
|
||||||
|
|
||||||
// Find the strength for each modulo number tick marker
|
|
||||||
int smallestTick = 0;
|
|
||||||
int biggestTick = TickSteps.Length - 1;
|
|
||||||
for (int i = TickSteps.Length - 1; i >= 0; i--)
|
|
||||||
{
|
{
|
||||||
// Calculate how far apart these modulo tick steps are spaced
|
var p = PointFromKeyframes(axis * tick, ref viewRect);
|
||||||
float tickSpacing = TickSteps[i] * pixelRange / range;
|
|
||||||
|
|
||||||
// Calculate the strength of the tick markers based on the spacing
|
// Draw line
|
||||||
_tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks));
|
var lineRect = new Rectangle
|
||||||
|
(
|
||||||
|
viewRect.Location + (p - 0.5f) * axis,
|
||||||
|
Float2.Lerp(viewRect.Size, Float2.One, axis)
|
||||||
|
);
|
||||||
|
Render2D.FillRectangle(lineRect, _linesColor.AlphaMultiplied(strength));
|
||||||
|
|
||||||
// Beyond threshold the ticks don't get any bigger or fatter
|
// Draw label
|
||||||
if (_tickStrengths[i] >= 1)
|
string label = tick.ToString(CultureInfo.InvariantCulture);
|
||||||
biggestTick = i;
|
var labelRect = new Rectangle
|
||||||
|
(
|
||||||
// Do not show small tick markers
|
viewRect.X + 4.0f + (p.X * axis.X),
|
||||||
if (tickSpacing <= minDistanceBetweenTicks)
|
viewRect.Y - LabelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X),
|
||||||
{
|
50,
|
||||||
smallestTick = i;
|
LabelsSize
|
||||||
break;
|
);
|
||||||
}
|
Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||||
}
|
}, TickSteps, ref _tickStrengths, min, max, pixelRange);
|
||||||
|
|
||||||
// Draw all tick levels
|
|
||||||
int tickLevels = biggestTick - smallestTick + 1;
|
|
||||||
for (int level = 0; level < tickLevels; level++)
|
|
||||||
{
|
|
||||||
float strength = _tickStrengths[smallestTick + level];
|
|
||||||
if (strength <= Mathf.Epsilon)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Draw all ticks
|
|
||||||
int l = Mathf.Clamp(smallestTick + level, 0, TickSteps.Length - 1);
|
|
||||||
int startTick = Mathf.FloorToInt(min / TickSteps[l]);
|
|
||||||
int endTick = Mathf.CeilToInt(max / TickSteps[l]);
|
|
||||||
for (int i = startTick; i <= endTick; i++)
|
|
||||||
{
|
|
||||||
if (l < biggestTick && (i % Mathf.RoundToInt(TickSteps[l + 1] / TickSteps[l]) == 0))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var tick = i * TickSteps[l];
|
|
||||||
var p = PointFromKeyframes(axis * tick, ref viewRect);
|
|
||||||
|
|
||||||
// Draw line
|
|
||||||
var lineRect = new Rectangle
|
|
||||||
(
|
|
||||||
viewRect.Location + (p - 0.5f) * axis,
|
|
||||||
Float2.Lerp(viewRect.Size, Float2.One, axis)
|
|
||||||
);
|
|
||||||
Render2D.FillRectangle(lineRect, _linesColor.AlphaMultiplied(strength));
|
|
||||||
|
|
||||||
// Draw label
|
|
||||||
string label = tick.ToString(CultureInfo.InvariantCulture);
|
|
||||||
var labelRect = new Rectangle
|
|
||||||
(
|
|
||||||
viewRect.X + 4.0f + (p.X * axis.X),
|
|
||||||
viewRect.Y - LabelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X),
|
|
||||||
50,
|
|
||||||
LabelsSize
|
|
||||||
);
|
|
||||||
Render2D.DrawText(_labelsFont, label, labelRect, _labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -890,9 +845,9 @@ namespace FlaxEditor.GUI
|
|||||||
Render2D.PushClip(ref viewRect);
|
Render2D.PushClip(ref viewRect);
|
||||||
|
|
||||||
if ((ShowAxes & UseMode.Vertical) == UseMode.Vertical)
|
if ((ShowAxes & UseMode.Vertical) == UseMode.Vertical)
|
||||||
DrawAxis(Float2.UnitX, ref viewRect, min.X, max.X, pixelRange.X);
|
DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X);
|
||||||
if ((ShowAxes & UseMode.Horizontal) == UseMode.Horizontal)
|
if ((ShowAxes & UseMode.Horizontal) == UseMode.Horizontal)
|
||||||
DrawAxis(Float2.UnitY, ref viewRect, min.Y, max.Y, pixelRange.Y);
|
DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y);
|
||||||
|
|
||||||
Render2D.PopClip();
|
Render2D.PopClip();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using FlaxEditor.Utilities;
|
using FlaxEditor.Utilities;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using Utils = FlaxEngine.Utils;
|
||||||
|
|
||||||
namespace FlaxEditor.GUI.Input
|
namespace FlaxEditor.GUI.Input
|
||||||
{
|
{
|
||||||
@@ -13,6 +14,8 @@ namespace FlaxEditor.GUI.Input
|
|||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
public class DoubleValueBox : ValueBox<double>
|
public class DoubleValueBox : ValueBox<double>
|
||||||
{
|
{
|
||||||
|
private Utils.ValueCategory _category = Utils.ValueCategory.None;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override double Value
|
public override double Value
|
||||||
{
|
{
|
||||||
@@ -129,10 +132,25 @@ namespace FlaxEditor.GUI.Input
|
|||||||
Value = Value;
|
Value = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the category of the value. This can be none for just a number or a more specific one like a distance.
|
||||||
|
/// </summary>
|
||||||
|
public Utils.ValueCategory Category
|
||||||
|
{
|
||||||
|
get => _category;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_category == value)
|
||||||
|
return;
|
||||||
|
_category = value;
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected sealed override void UpdateText()
|
protected sealed override void UpdateText()
|
||||||
{
|
{
|
||||||
SetText(Utilities.Utils.FormatFloat(_value));
|
SetText(Utilities.Utils.FormatFloat(_value, Category));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
|
||||||
using FlaxEditor.Utilities;
|
using FlaxEditor.Utilities;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using Utils = FlaxEngine.Utils;
|
||||||
|
|
||||||
namespace FlaxEditor.GUI.Input
|
namespace FlaxEditor.GUI.Input
|
||||||
{
|
{
|
||||||
@@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input
|
|||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
public class FloatValueBox : ValueBox<float>
|
public class FloatValueBox : ValueBox<float>
|
||||||
{
|
{
|
||||||
|
private Utils.ValueCategory _category = Utils.ValueCategory.None;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override float Value
|
public override float Value
|
||||||
{
|
{
|
||||||
@@ -137,10 +139,25 @@ namespace FlaxEditor.GUI.Input
|
|||||||
Value = Value;
|
Value = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the category of the value. This can be none for just a number or a more specific one like a distance.
|
||||||
|
/// </summary>
|
||||||
|
public Utils.ValueCategory Category
|
||||||
|
{
|
||||||
|
get => _category;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_category == value)
|
||||||
|
return;
|
||||||
|
_category = value;
|
||||||
|
UpdateText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected sealed override void UpdateText()
|
protected sealed override void UpdateText()
|
||||||
{
|
{
|
||||||
SetText(Utilities.Utils.FormatFloat(_value));
|
SetText(Utilities.Utils.FormatFloat(_value, Category));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
{
|
{
|
||||||
_timeline = timeline;
|
_timeline = timeline;
|
||||||
_tickSteps = Utilities.Utils.CurveTickSteps;
|
_tickSteps = Utilities.Utils.CurveTickSteps;
|
||||||
_tickStrengths = new float[_tickSteps.Length];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateSelectionRectangle()
|
private void UpdateSelectionRectangle()
|
||||||
@@ -173,55 +172,20 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
var rightFrame = Mathf.Ceil((right - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond;
|
var rightFrame = Mathf.Ceil((right - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond;
|
||||||
var min = leftFrame;
|
var min = leftFrame;
|
||||||
var max = rightFrame;
|
var max = rightFrame;
|
||||||
int smallestTick = 0;
|
|
||||||
int biggestTick = _tickSteps.Length - 1;
|
|
||||||
for (int i = _tickSteps.Length - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
// Calculate how far apart these modulo tick steps are spaced
|
|
||||||
float tickSpacing = _tickSteps[i] * _timeline.Zoom;
|
|
||||||
|
|
||||||
// Calculate the strength of the tick markers based on the spacing
|
|
||||||
_tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks));
|
|
||||||
|
|
||||||
// Beyond threshold the ticks don't get any bigger or fatter
|
|
||||||
if (_tickStrengths[i] >= 1)
|
|
||||||
biggestTick = i;
|
|
||||||
|
|
||||||
// Do not show small tick markers
|
|
||||||
if (tickSpacing <= minDistanceBetweenTicks)
|
|
||||||
{
|
|
||||||
smallestTick = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int tickLevels = biggestTick - smallestTick + 1;
|
|
||||||
|
|
||||||
// Draw vertical lines for time axis
|
// Draw vertical lines for time axis
|
||||||
for (int level = 0; level < tickLevels; level++)
|
var pixelsInRange = _timeline.Zoom;
|
||||||
|
var pixelRange = pixelsInRange * (max - min);
|
||||||
|
var tickRange = Utilities.Utils.DrawCurveTicks((float tick, float strength) =>
|
||||||
{
|
{
|
||||||
float strength = _tickStrengths[smallestTick + level];
|
var time = tick / _timeline.FramesPerSecond;
|
||||||
if (strength <= Mathf.Epsilon)
|
var x = time * zoom + Timeline.StartOffset;
|
||||||
continue;
|
var lineColor = style.ForegroundDisabled.RGBMultiplied(0.7f).AlphaMultiplied(strength);
|
||||||
|
Render2D.FillRectangle(new Rectangle(x - 0.5f, 0, 1.0f, height), lineColor);
|
||||||
// Draw all ticks
|
}, _tickSteps, ref _tickStrengths, min, max, pixelRange, minDistanceBetweenTicks, maxDistanceBetweenTicks);
|
||||||
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
|
var smallestTick = tickRange.X;
|
||||||
var lStep = _tickSteps[l];
|
var biggestTick = tickRange.Y;
|
||||||
var lNextStep = _tickSteps[l + 1];
|
var tickLevels = biggestTick - smallestTick + 1;
|
||||||
int startTick = Mathf.FloorToInt(min / lStep);
|
|
||||||
int endTick = Mathf.CeilToInt(max / lStep);
|
|
||||||
Color lineColor = style.ForegroundDisabled.RGBMultiplied(0.7f).AlphaMultiplied(strength);
|
|
||||||
for (int i = startTick; i <= endTick; i++)
|
|
||||||
{
|
|
||||||
if (l < biggestTick && (i % Mathf.RoundToInt(lNextStep / lStep) == 0))
|
|
||||||
continue;
|
|
||||||
var tick = i * lStep;
|
|
||||||
var time = tick / _timeline.FramesPerSecond;
|
|
||||||
var x = time * zoom + Timeline.StartOffset;
|
|
||||||
|
|
||||||
// Draw line
|
|
||||||
Render2D.FillRectangle(new Rectangle(x - 0.5f, 0, 1.0f, height), lineColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw selection rectangle
|
// Draw selection rectangle
|
||||||
if (_isSelecting)
|
if (_isSelecting)
|
||||||
|
|||||||
@@ -904,7 +904,7 @@ namespace FlaxEditor.GUI
|
|||||||
var k = new Keyframe
|
var k = new Keyframe
|
||||||
{
|
{
|
||||||
Time = keyframesPos.X,
|
Time = keyframesPos.X,
|
||||||
Value = DefaultValue,
|
Value = Utilities.Utils.CloneValue(DefaultValue),
|
||||||
};
|
};
|
||||||
OnEditingStart();
|
OnEditingStart();
|
||||||
AddKeyframe(k);
|
AddKeyframe(k);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using FlaxEditor.GUI.Timeline.Undo;
|
using FlaxEditor.GUI.Timeline.Undo;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
@@ -44,6 +46,25 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
var thickness = 2.0f;
|
var thickness = 2.0f;
|
||||||
var borderColor = _isMoving ? moveColor : (IsMouseOver && _canEdit ? Color.Yellow : style.BorderNormal);
|
var borderColor = _isMoving ? moveColor : (IsMouseOver && _canEdit ? Color.Yellow : style.BorderNormal);
|
||||||
Render2D.FillRectangle(new Rectangle((Width - thickness) * 0.5f, timeAxisHeaderOffset, thickness, Height - timeAxisHeaderOffset), borderColor);
|
Render2D.FillRectangle(new Rectangle((Width - thickness) * 0.5f, timeAxisHeaderOffset, thickness, Height - timeAxisHeaderOffset), borderColor);
|
||||||
|
if (_canEdit && _isMoving)
|
||||||
|
{
|
||||||
|
// TODO: handle start
|
||||||
|
string labelText;
|
||||||
|
switch (_timeline.TimeShowMode)
|
||||||
|
{
|
||||||
|
case Timeline.TimeShowModes.Frames:
|
||||||
|
labelText = _timeline.DurationFrames.ToString("###0", CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case Timeline.TimeShowModes.Seconds:
|
||||||
|
labelText = _timeline.Duration.ToString("###0.##'s'", CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case Timeline.TimeShowModes.Time:
|
||||||
|
labelText = TimeSpan.FromSeconds(_timeline.DurationFrames / _timeline.FramesPerSecond).ToString("g");
|
||||||
|
break;
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
Render2D.DrawText(style.FontSmall, labelText, style.Foreground, new Float2((Width - thickness) * 0.5f + 4, timeAxisHeaderOffset));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -90,13 +111,26 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
{
|
{
|
||||||
_timeline.MarkAsEdited();
|
_timeline.MarkAsEdited();
|
||||||
}
|
}
|
||||||
|
Cursor = CursorType.SizeWE;
|
||||||
|
}
|
||||||
|
else if (IsMouseOver && _canEdit)
|
||||||
|
{
|
||||||
|
Cursor = CursorType.SizeWE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
base.OnMouseMove(location);
|
base.OnMouseMove(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnMouseLeave()
|
||||||
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
base.OnMouseLeave();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
{
|
{
|
||||||
@@ -127,6 +161,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
{
|
{
|
||||||
EndMoving();
|
EndMoving();
|
||||||
}
|
}
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
|
||||||
base.OnLostFocus();
|
base.OnLostFocus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1141,17 +1141,19 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
{
|
{
|
||||||
foreach (var e in _playbackNavigation)
|
foreach (var e in _playbackNavigation)
|
||||||
{
|
{
|
||||||
e.Enabled = false;
|
e.Enabled = true;
|
||||||
e.Visible = false;
|
e.Visible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_playbackStop != null)
|
if (_playbackStop != null)
|
||||||
{
|
{
|
||||||
_playbackStop.Visible = false;
|
_playbackStop.Visible = true;
|
||||||
|
_playbackStop.Enabled = false;
|
||||||
}
|
}
|
||||||
if (_playbackPlay != null)
|
if (_playbackPlay != null)
|
||||||
{
|
{
|
||||||
_playbackPlay.Visible = false;
|
_playbackPlay.Visible = true;
|
||||||
|
_playbackPlay.Enabled = false;
|
||||||
}
|
}
|
||||||
if (_positionHandle != null)
|
if (_positionHandle != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
b.TooltipText = Utilities.Utils.GetTooltip(actorNode.Actor);
|
b.TooltipText = Utilities.Utils.GetTooltip(actorNode.Actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
menu.AddButton("Select...", OnClickedSelect).TooltipText = "Opens actor picker dialog to select the target actor for this track";
|
menu.AddButton("Retarget...", OnClickedSelect).TooltipText = "Opens actor picker dialog to select the target actor for this track";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ using System.Reflection;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using FlaxEditor.GUI.Timeline.Undo;
|
using FlaxEditor.GUI.Timeline.Undo;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Utilities;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
@@ -56,7 +58,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
throw new Exception("Invalid track data.");
|
throw new Exception("Invalid track data.");
|
||||||
|
|
||||||
var keyframes = new KeyframesEditor.Keyframe[keyframesCount];
|
var keyframes = new KeyframesEditor.Keyframe[keyframesCount];
|
||||||
var dataBuffer = new byte[e.ValueSize];
|
|
||||||
var propertyType = TypeUtils.GetManagedType(e.MemberTypeName);
|
var propertyType = TypeUtils.GetManagedType(e.MemberTypeName);
|
||||||
if (propertyType == null)
|
if (propertyType == null)
|
||||||
{
|
{
|
||||||
@@ -67,20 +68,40 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GCHandle handle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned);
|
if (e.ValueSize != 0)
|
||||||
for (int i = 0; i < keyframesCount; i++)
|
|
||||||
{
|
{
|
||||||
var time = stream.ReadSingle();
|
// POD value type - use raw memory
|
||||||
stream.Read(dataBuffer, 0, e.ValueSize);
|
var dataBuffer = new byte[e.ValueSize];
|
||||||
var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType);
|
GCHandle handle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned);
|
||||||
|
for (int i = 0; i < keyframesCount; i++)
|
||||||
keyframes[i] = new KeyframesEditor.Keyframe
|
|
||||||
{
|
{
|
||||||
Time = time,
|
var time = stream.ReadSingle();
|
||||||
Value = value,
|
stream.Read(dataBuffer, 0, e.ValueSize);
|
||||||
};
|
var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType);
|
||||||
|
|
||||||
|
keyframes[i] = new KeyframesEditor.Keyframe
|
||||||
|
{
|
||||||
|
Time = time,
|
||||||
|
Value = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Generic value - use Json storage (as UTF-8)
|
||||||
|
for (int i = 0; i < keyframesCount; i++)
|
||||||
|
{
|
||||||
|
var time = stream.ReadSingle();
|
||||||
|
var len = stream.ReadInt32();
|
||||||
|
var value = len != 0 ? FlaxEngine.Json.JsonSerializer.Deserialize(Encoding.UTF8.GetString(stream.ReadBytes(len)), propertyType) : null;
|
||||||
|
keyframes[i] = new KeyframesEditor.Keyframe
|
||||||
|
{
|
||||||
|
Time = time,
|
||||||
|
Value = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handle.Free();
|
|
||||||
|
|
||||||
e.Keyframes.DefaultValue = e.GetDefaultValue(propertyType);
|
e.Keyframes.DefaultValue = e.GetDefaultValue(propertyType);
|
||||||
e.Keyframes.SetKeyframes(keyframes);
|
e.Keyframes.SetKeyframes(keyframes);
|
||||||
@@ -113,17 +134,35 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
stream.Write(propertyTypeNameData);
|
stream.Write(propertyTypeNameData);
|
||||||
stream.Write('\0');
|
stream.Write('\0');
|
||||||
|
|
||||||
var dataBuffer = new byte[e.ValueSize];
|
if (e.ValueSize != 0)
|
||||||
IntPtr ptr = Marshal.AllocHGlobal(e.ValueSize);
|
|
||||||
for (int i = 0; i < keyframes.Count; i++)
|
|
||||||
{
|
{
|
||||||
var keyframe = keyframes[i];
|
// POD value type - use raw memory
|
||||||
Marshal.StructureToPtr(keyframe.Value, ptr, true);
|
var dataBuffer = new byte[e.ValueSize];
|
||||||
Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize);
|
IntPtr ptr = Marshal.AllocHGlobal(e.ValueSize);
|
||||||
stream.Write(keyframe.Time);
|
for (int i = 0; i < keyframes.Count; i++)
|
||||||
stream.Write(dataBuffer);
|
{
|
||||||
|
var keyframe = keyframes[i];
|
||||||
|
Marshal.StructureToPtr(keyframe.Value, ptr, true);
|
||||||
|
Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize);
|
||||||
|
stream.Write(keyframe.Time);
|
||||||
|
stream.Write(dataBuffer);
|
||||||
|
}
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Generic value - use Json storage (as UTF-8)
|
||||||
|
for (int i = 0; i < keyframes.Count; i++)
|
||||||
|
{
|
||||||
|
var keyframe = keyframes[i];
|
||||||
|
stream.Write(keyframe.Time);
|
||||||
|
var json = keyframe.Value != null ? FlaxEngine.Json.JsonSerializer.Serialize(keyframe.Value) : null;
|
||||||
|
var len = json?.Length ?? 0;
|
||||||
|
stream.Write(len);
|
||||||
|
if (len > 0)
|
||||||
|
stream.Write(Encoding.UTF8.GetBytes(json));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Marshal.FreeHGlobal(ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] _keyframesEditingStartData;
|
private byte[] _keyframesEditingStartData;
|
||||||
@@ -281,7 +320,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
private void OnKeyframesEditingEnd()
|
private void OnKeyframesEditingEnd()
|
||||||
{
|
{
|
||||||
var after = EditTrackAction.CaptureData(this);
|
var after = EditTrackAction.CaptureData(this);
|
||||||
if (!Utils.ArraysEqual(_keyframesEditingStartData, after))
|
if (!FlaxEngine.Utils.ArraysEqual(_keyframesEditingStartData, after))
|
||||||
Timeline.AddBatchedUndoAction(new EditTrackAction(Timeline, this, _keyframesEditingStartData, after));
|
Timeline.AddBatchedUndoAction(new EditTrackAction(Timeline, this, _keyframesEditingStartData, after));
|
||||||
_keyframesEditingStartData = null;
|
_keyframesEditingStartData = null;
|
||||||
}
|
}
|
||||||
@@ -308,7 +347,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// <returns>The default value.</returns>
|
/// <returns>The default value.</returns>
|
||||||
protected virtual object GetDefaultValue(Type propertyType)
|
protected virtual object GetDefaultValue(Type propertyType)
|
||||||
{
|
{
|
||||||
return Activator.CreateInstance(propertyType);
|
var value = TypeUtils.GetDefaultValue(new ScriptType(propertyType));
|
||||||
|
if (value == null)
|
||||||
|
value = Activator.CreateInstance(propertyType);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -424,6 +424,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
{ typeof(Guid), KeyframesPropertyTrack.GetArchetype() },
|
{ typeof(Guid), KeyframesPropertyTrack.GetArchetype() },
|
||||||
{ typeof(DateTime), KeyframesPropertyTrack.GetArchetype() },
|
{ typeof(DateTime), KeyframesPropertyTrack.GetArchetype() },
|
||||||
{ typeof(TimeSpan), KeyframesPropertyTrack.GetArchetype() },
|
{ typeof(TimeSpan), KeyframesPropertyTrack.GetArchetype() },
|
||||||
|
{ typeof(LocalizedString), KeyframesPropertyTrack.GetArchetype() },
|
||||||
{ typeof(string), StringPropertyTrack.GetArchetype() },
|
{ typeof(string), StringPropertyTrack.GetArchetype() },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,5 +161,20 @@ namespace FlaxEditor.Gizmo
|
|||||||
}
|
}
|
||||||
throw new ArgumentException("Not added mode to activate.");
|
throw new ArgumentException("Not added mode to activate.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the gizmo of a given type or returns null if not added.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of the gizmo.</typeparam>
|
||||||
|
/// <returns>Found gizmo or null.</returns>
|
||||||
|
public T Get<T>() where T : GizmoBase
|
||||||
|
{
|
||||||
|
foreach (var e in this)
|
||||||
|
{
|
||||||
|
if (e is T asT)
|
||||||
|
return asT;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ namespace FlaxEditor.Gizmo
|
|||||||
var plane = new Plane(Vector3.Zero, Vector3.UnitY);
|
var plane = new Plane(Vector3.Zero, Vector3.UnitY);
|
||||||
var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos);
|
var dst = CollisionsHelper.DistancePlanePoint(ref plane, ref viewPos);
|
||||||
|
|
||||||
float space = Editor.Instance.Options.Options.Viewport.ViewportGridScale, size;
|
var options = Editor.Instance.Options.Options;
|
||||||
|
float space = options.Viewport.ViewportGridScale, size;
|
||||||
if (dst <= 500.0f)
|
if (dst <= 500.0f)
|
||||||
{
|
{
|
||||||
size = 8000;
|
size = 8000;
|
||||||
@@ -62,8 +63,12 @@ namespace FlaxEditor.Gizmo
|
|||||||
size = 100000;
|
size = 100000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color color = Color.Gray * 0.7f;
|
float bigLineIntensity = 0.8f;
|
||||||
|
Color bigColor = Color.Gray * bigLineIntensity;
|
||||||
|
Color color = bigColor * 0.8f;
|
||||||
int count = (int)(size / space);
|
int count = (int)(size / space);
|
||||||
|
int midLine = count / 2;
|
||||||
|
int bigLinesMod = count / 8;
|
||||||
|
|
||||||
Vector3 start = new Vector3(0, 0, size * -0.5f);
|
Vector3 start = new Vector3(0, 0, size * -0.5f);
|
||||||
Vector3 end = new Vector3(0, 0, size * 0.5f);
|
Vector3 end = new Vector3(0, 0, size * 0.5f);
|
||||||
@@ -71,7 +76,12 @@ namespace FlaxEditor.Gizmo
|
|||||||
for (int i = 0; i <= count; i++)
|
for (int i = 0; i <= count; i++)
|
||||||
{
|
{
|
||||||
start.X = end.X = i * space + start.Z;
|
start.X = end.X = i * space + start.Z;
|
||||||
DebugDraw.DrawLine(start, end, color);
|
Color lineColor = color;
|
||||||
|
if (i == midLine)
|
||||||
|
lineColor = Color.Blue * bigLineIntensity;
|
||||||
|
else if (i % bigLinesMod == 0)
|
||||||
|
lineColor = bigColor;
|
||||||
|
DebugDraw.DrawLine(start, end, lineColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
start = new Vector3(size * -0.5f, 0, 0);
|
start = new Vector3(size * -0.5f, 0, 0);
|
||||||
@@ -80,7 +90,12 @@ namespace FlaxEditor.Gizmo
|
|||||||
for (int i = 0; i <= count; i++)
|
for (int i = 0; i <= count; i++)
|
||||||
{
|
{
|
||||||
start.Z = end.Z = i * space + start.X;
|
start.Z = end.Z = i * space + start.X;
|
||||||
DebugDraw.DrawLine(start, end, color);
|
Color lineColor = color;
|
||||||
|
if (i == midLine)
|
||||||
|
lineColor = Color.Red * bigLineIntensity;
|
||||||
|
else if (i % bigLinesMod == 0)
|
||||||
|
lineColor = bigColor;
|
||||||
|
DebugDraw.DrawLine(start, end, lineColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugDraw.Draw(ref renderContext, input.View(), null, true);
|
DebugDraw.Draw(ref renderContext, input.View(), null, true);
|
||||||
|
|||||||
@@ -117,5 +117,10 @@ namespace FlaxEditor.Gizmo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="actor">The new actor to spawn.</param>
|
/// <param name="actor">The new actor to spawn.</param>
|
||||||
void Spawn(Actor actor);
|
void Spawn(Actor actor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the context menu at the current mouse location (using current selection).
|
||||||
|
/// </summary>
|
||||||
|
void OpenContextMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ namespace FlaxEditor.Gizmo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Action Duplicate;
|
public Action Duplicate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the array of selected objects.
|
||||||
|
/// </summary>
|
||||||
|
public List<SceneGraphNode> Selection => _selection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the array of selected parent objects (as actors).
|
/// Gets the array of selected parent objects (as actors).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
891
Source/Editor/Gizmo/UIEditorGizmo.cs
Normal file
891
Source/Editor/Gizmo/UIEditorGizmo.cs
Normal file
@@ -0,0 +1,891 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FlaxEditor.Gizmo;
|
||||||
|
using FlaxEditor.SceneGraph;
|
||||||
|
using FlaxEditor.SceneGraph.Actors;
|
||||||
|
using FlaxEditor.Viewport.Cameras;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
|
namespace FlaxEditor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UI editor camera.
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
internal sealed class UIEditorCamera : ViewportCamera
|
||||||
|
{
|
||||||
|
public UIEditorRoot UIEditor;
|
||||||
|
|
||||||
|
public void ShowActors(IEnumerable<Actor> actors)
|
||||||
|
{
|
||||||
|
var root = UIEditor.UIRoot;
|
||||||
|
if (root == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Calculate bounds of all selected objects
|
||||||
|
var areaRect = Rectangle.Empty;
|
||||||
|
foreach (var actor in actors)
|
||||||
|
{
|
||||||
|
Rectangle bounds;
|
||||||
|
if (actor is UIControl uiControl && uiControl.HasControl && uiControl.IsActive)
|
||||||
|
{
|
||||||
|
var control = uiControl.Control;
|
||||||
|
bounds = control.EditorBounds;
|
||||||
|
|
||||||
|
var ul = control.PointToParent(root, bounds.UpperLeft);
|
||||||
|
var ur = control.PointToParent(root, bounds.UpperRight);
|
||||||
|
var bl = control.PointToParent(root, bounds.BottomLeft);
|
||||||
|
var br = control.PointToParent(root, bounds.BottomRight);
|
||||||
|
|
||||||
|
var min = Float2.Min(Float2.Min(ul, ur), Float2.Min(bl, br));
|
||||||
|
var max = Float2.Max(Float2.Max(ul, ur), Float2.Max(bl, br));
|
||||||
|
bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero));
|
||||||
|
}
|
||||||
|
else if (actor is UICanvas uiCanvas && uiCanvas.IsActive && uiCanvas.GUI.Parent == root)
|
||||||
|
{
|
||||||
|
bounds = uiCanvas.GUI.Bounds;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (areaRect == Rectangle.Empty)
|
||||||
|
areaRect = bounds;
|
||||||
|
else
|
||||||
|
areaRect = Rectangle.Union(areaRect, bounds);
|
||||||
|
}
|
||||||
|
if (areaRect == Rectangle.Empty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Add margin
|
||||||
|
areaRect = areaRect.MakeExpanded(100.0f);
|
||||||
|
|
||||||
|
// Show bounds
|
||||||
|
UIEditor.ViewScale = (UIEditor.Size / areaRect.Size).MinValue * 0.95f;
|
||||||
|
UIEditor.ViewCenterPosition = areaRect.Center;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void FocusSelection(GizmosCollection gizmos, ref Quaternion orientation)
|
||||||
|
{
|
||||||
|
ShowActors(gizmos.Get<TransformGizmo>().Selection, ref orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ShowActor(Actor actor)
|
||||||
|
{
|
||||||
|
ShowActors(new[] { actor });
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ShowActors(List<SceneGraphNode> selection, ref Quaternion orientation)
|
||||||
|
{
|
||||||
|
ShowActors(selection.ConvertAll(x => (Actor)x.EditableObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateView(float dt, ref Vector3 moveDelta, ref Float2 mouseDelta, out bool centerMouse)
|
||||||
|
{
|
||||||
|
centerMouse = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Root control for UI Controls presentation in the game/prefab viewport.
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
internal class UIEditorRoot : InputsPassThrough
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// View for the UI structure to be linked in for camera zoom and panning operations.
|
||||||
|
/// </summary>
|
||||||
|
private sealed class View : ContainerControl
|
||||||
|
{
|
||||||
|
public View(UIEditorRoot parent)
|
||||||
|
{
|
||||||
|
AutoFocus = false;
|
||||||
|
ClipChildren = false;
|
||||||
|
CullChildren = false;
|
||||||
|
Pivot = Float2.Zero;
|
||||||
|
Size = new Float2(1920, 1080);
|
||||||
|
Parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool RayCast(ref Float2 location, out Control hit)
|
||||||
|
{
|
||||||
|
// Ignore self
|
||||||
|
return RayCastChildren(ref location, out hit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
|
||||||
|
{
|
||||||
|
location = PointFromParent(ref locationParent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawSelf()
|
||||||
|
{
|
||||||
|
var uiRoot = (UIEditorRoot)Parent;
|
||||||
|
if (!uiRoot.EnableBackground)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Draw canvas area
|
||||||
|
var bounds = new Rectangle(Float2.Zero, Size);
|
||||||
|
Render2D.FillRectangle(bounds, new Color(0, 0, 0, 0.2f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cached placement of the widget used to size/edit control
|
||||||
|
/// </summary>
|
||||||
|
private struct Widget
|
||||||
|
{
|
||||||
|
public UIControl UIControl;
|
||||||
|
public Rectangle Bounds;
|
||||||
|
public Float2 ResizeAxis;
|
||||||
|
public CursorType Cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _mouseMovesControl, _mouseMovesView, _mouseMovesWidget;
|
||||||
|
private Float2 _mouseMovesPos, _moveSnapDelta;
|
||||||
|
private float _mouseMoveSum;
|
||||||
|
private UndoMultiBlock _undoBlock;
|
||||||
|
private View _view;
|
||||||
|
private float[] _gridTickSteps = Utilities.Utils.CurveTickSteps, _gridTickStrengths;
|
||||||
|
private List<Widget> _widgets;
|
||||||
|
private Widget _activeWidget;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if enable displaying UI editing background and grid elements.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool EnableBackground => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if enable selecting controls with mouse button.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool EnableSelecting => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if enable panning and zooming the view.
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableCamera => _view != null && EnableBackground;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transform gizmo to use sync with (selection, snapping, transformation settings).
|
||||||
|
/// </summary>
|
||||||
|
public virtual TransformGizmo TransformGizmo => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The root control for controls to be linked in.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ContainerControl UIRoot;
|
||||||
|
|
||||||
|
internal Float2 ViewPosition
|
||||||
|
{
|
||||||
|
get => _view.Location / -ViewScale;
|
||||||
|
set => _view.Location = value * -ViewScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Float2 ViewCenterPosition
|
||||||
|
{
|
||||||
|
get => (_view.Location - Size * 0.5f) / -ViewScale;
|
||||||
|
set => _view.Location = Size * 0.5f + value * -ViewScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal float ViewScale
|
||||||
|
{
|
||||||
|
get => _view?.Scale.X ?? 1;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_view == null)
|
||||||
|
return;
|
||||||
|
value = Mathf.Clamp(value, 0.1f, 4.0f);
|
||||||
|
_view.Scale = new Float2(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UIEditorRoot(bool enableCamera = false)
|
||||||
|
{
|
||||||
|
AnchorPreset = AnchorPresets.StretchAll;
|
||||||
|
Offsets = Margin.Zero;
|
||||||
|
AutoFocus = false;
|
||||||
|
UIRoot = this;
|
||||||
|
CullChildren = false;
|
||||||
|
ClipChildren = true;
|
||||||
|
if (enableCamera)
|
||||||
|
{
|
||||||
|
_view = new View(this);
|
||||||
|
UIRoot = _view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDown(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var transformGizmo = TransformGizmo;
|
||||||
|
var owner = transformGizmo?.Owner;
|
||||||
|
if (_widgets != null && _widgets.Count != 0 && button == MouseButton.Left)
|
||||||
|
{
|
||||||
|
foreach (var widget in _widgets)
|
||||||
|
{
|
||||||
|
if (widget.Bounds.Contains(ref location))
|
||||||
|
{
|
||||||
|
// Initialize widget movement
|
||||||
|
_activeWidget = widget;
|
||||||
|
_mouseMovesWidget = true;
|
||||||
|
_mouseMovesPos = location;
|
||||||
|
Cursor = widget.Cursor;
|
||||||
|
StartUndo();
|
||||||
|
Focus();
|
||||||
|
StartMouseCapture();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (EnableSelecting && owner != null && !_mouseMovesControl && button == MouseButton.Left)
|
||||||
|
{
|
||||||
|
// Raycast the control under the mouse
|
||||||
|
var mousePos = PointFromWindow(RootWindow.MousePosition);
|
||||||
|
if (RayCastControl(ref mousePos, out var hitControl))
|
||||||
|
{
|
||||||
|
var uiControlNode = FindUIControlNode(hitControl);
|
||||||
|
if (uiControlNode != null)
|
||||||
|
{
|
||||||
|
// Select node (with additive mode)
|
||||||
|
var selection = new List<SceneGraphNode>();
|
||||||
|
if (Root.GetKey(KeyboardKeys.Control))
|
||||||
|
{
|
||||||
|
// Add/remove from selection
|
||||||
|
selection.AddRange(transformGizmo.Selection);
|
||||||
|
if (transformGizmo.Selection.Contains(uiControlNode))
|
||||||
|
selection.Remove(uiControlNode);
|
||||||
|
else
|
||||||
|
selection.Add(uiControlNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Select
|
||||||
|
selection.Add(uiControlNode);
|
||||||
|
}
|
||||||
|
owner.Select(selection);
|
||||||
|
|
||||||
|
// Initialize control movement
|
||||||
|
_mouseMovesControl = true;
|
||||||
|
_mouseMovesPos = location;
|
||||||
|
_mouseMoveSum = 0.0f;
|
||||||
|
_moveSnapDelta = Float2.Zero;
|
||||||
|
Focus();
|
||||||
|
StartMouseCapture();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Allow deselecting if user clicks on nothing
|
||||||
|
else
|
||||||
|
{
|
||||||
|
owner.Select(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (EnableCamera && (button == MouseButton.Right || button == MouseButton.Middle))
|
||||||
|
{
|
||||||
|
// Initialize surface movement
|
||||||
|
_mouseMovesView = true;
|
||||||
|
_mouseMovesPos = location;
|
||||||
|
_mouseMoveSum = 0.0f;
|
||||||
|
Focus();
|
||||||
|
StartMouseCapture();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Focus(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMouseMove(Float2 location)
|
||||||
|
{
|
||||||
|
base.OnMouseMove(location);
|
||||||
|
|
||||||
|
// Change cursor if mouse is over active control widget
|
||||||
|
bool cursorChanged = false;
|
||||||
|
if (_widgets != null && _widgets.Count != 0 && !_mouseMovesControl && !_mouseMovesWidget && !_mouseMovesView)
|
||||||
|
{
|
||||||
|
foreach (var widget in _widgets)
|
||||||
|
{
|
||||||
|
if (widget.Bounds.Contains(ref location))
|
||||||
|
{
|
||||||
|
Cursor = widget.Cursor;
|
||||||
|
cursorChanged = true;
|
||||||
|
}
|
||||||
|
else if (Cursor != CursorType.Default && !cursorChanged)
|
||||||
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var transformGizmo = TransformGizmo;
|
||||||
|
if (_mouseMovesControl && transformGizmo != null)
|
||||||
|
{
|
||||||
|
// Calculate transform delta
|
||||||
|
var delta = location - _mouseMovesPos;
|
||||||
|
if (transformGizmo.TranslationSnapEnable || transformGizmo.Owner.UseSnapping)
|
||||||
|
{
|
||||||
|
_moveSnapDelta += delta;
|
||||||
|
delta = Float2.SnapToGrid(_moveSnapDelta, new Float2(transformGizmo.TranslationSnapValue * ViewScale));
|
||||||
|
_moveSnapDelta -= delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move selected controls
|
||||||
|
if (delta.LengthSquared > 0.0f)
|
||||||
|
{
|
||||||
|
StartUndo();
|
||||||
|
var moved = false;
|
||||||
|
var moveLocation = _mouseMovesPos + delta;
|
||||||
|
var selection = transformGizmo.Selection;
|
||||||
|
for (var i = 0; i < selection.Count; i++)
|
||||||
|
{
|
||||||
|
if (IsValidControl(selection[i], out var uiControl))
|
||||||
|
{
|
||||||
|
// Move control (handle any control transformations by moving in editor's local-space)
|
||||||
|
var control = uiControl.Control;
|
||||||
|
var localLocation = control.LocalLocation;
|
||||||
|
var uiControlDelta = GetControlDelta(control, ref _mouseMovesPos, ref moveLocation);
|
||||||
|
control.LocalLocation = localLocation + uiControlDelta;
|
||||||
|
|
||||||
|
// Don't move if layout doesn't allow it
|
||||||
|
if (control.Parent != null)
|
||||||
|
control.Parent.PerformLayout();
|
||||||
|
else
|
||||||
|
control.PerformLayout();
|
||||||
|
|
||||||
|
// Check if control was moved (parent container could block it)
|
||||||
|
if (localLocation != control.LocalLocation)
|
||||||
|
moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_mouseMovesPos = location;
|
||||||
|
_mouseMoveSum += delta.Length;
|
||||||
|
if (moved)
|
||||||
|
Cursor = CursorType.SizeAll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_mouseMovesWidget && _activeWidget.UIControl)
|
||||||
|
{
|
||||||
|
// Calculate transform delta
|
||||||
|
var resizeAxisAbs = _activeWidget.ResizeAxis.Absolute;
|
||||||
|
var resizeAxisPos = Float2.Clamp(_activeWidget.ResizeAxis, Float2.Zero, Float2.One);
|
||||||
|
var resizeAxisNeg = Float2.Clamp(-_activeWidget.ResizeAxis, Float2.Zero, Float2.One);
|
||||||
|
var delta = location - _mouseMovesPos;
|
||||||
|
// TODO: scale/size snapping?
|
||||||
|
delta *= resizeAxisAbs;
|
||||||
|
|
||||||
|
// Resize control via widget
|
||||||
|
var moveLocation = _mouseMovesPos + delta;
|
||||||
|
var control = _activeWidget.UIControl.Control;
|
||||||
|
var uiControlDelta = GetControlDelta(control, ref _mouseMovesPos, ref moveLocation);
|
||||||
|
control.LocalLocation += uiControlDelta * resizeAxisNeg;
|
||||||
|
control.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg;
|
||||||
|
|
||||||
|
// Don't move if layout doesn't allow it
|
||||||
|
if (control.Parent != null)
|
||||||
|
control.Parent.PerformLayout();
|
||||||
|
else
|
||||||
|
control.PerformLayout();
|
||||||
|
|
||||||
|
_mouseMovesPos = location;
|
||||||
|
}
|
||||||
|
if (_mouseMovesView)
|
||||||
|
{
|
||||||
|
// Move view
|
||||||
|
var delta = location - _mouseMovesPos;
|
||||||
|
if (delta.LengthSquared > 4.0f)
|
||||||
|
{
|
||||||
|
_mouseMovesPos = location;
|
||||||
|
_mouseMoveSum += delta.Length;
|
||||||
|
_view.Location += delta;
|
||||||
|
Cursor = CursorType.SizeAll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
EndMovingControls();
|
||||||
|
EndMovingWidget();
|
||||||
|
if (_mouseMovesView)
|
||||||
|
{
|
||||||
|
EndMovingView();
|
||||||
|
if (button == MouseButton.Right && _mouseMoveSum < 2.0f)
|
||||||
|
TransformGizmo.Owner.OpenContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnMouseUp(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMouseLeave()
|
||||||
|
{
|
||||||
|
EndMovingControls();
|
||||||
|
EndMovingView();
|
||||||
|
EndMovingWidget();
|
||||||
|
|
||||||
|
base.OnMouseLeave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLostFocus()
|
||||||
|
{
|
||||||
|
EndMovingControls();
|
||||||
|
EndMovingView();
|
||||||
|
EndMovingWidget();
|
||||||
|
|
||||||
|
base.OnLostFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseWheel(Float2 location, float delta)
|
||||||
|
{
|
||||||
|
if (base.OnMouseWheel(location, delta))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (EnableCamera && !_mouseMovesControl)
|
||||||
|
{
|
||||||
|
// Zoom view
|
||||||
|
var nextViewScale = ViewScale + delta * 0.1f;
|
||||||
|
if (delta > 0 && !_mouseMovesControl)
|
||||||
|
{
|
||||||
|
// Scale towards mouse when zooming in
|
||||||
|
var nextCenterPosition = ViewPosition + location / ViewScale;
|
||||||
|
ViewScale = nextViewScale;
|
||||||
|
ViewPosition = nextCenterPosition - (location / ViewScale);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Scale while keeping center position when zooming out or when dragging view
|
||||||
|
var viewCenter = ViewCenterPosition;
|
||||||
|
ViewScale = nextViewScale;
|
||||||
|
ViewCenterPosition = viewCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
if (EnableBackground && _view != null)
|
||||||
|
{
|
||||||
|
// Draw background
|
||||||
|
Surface.VisjectSurface.DrawBackgroundDefault(Editor.Instance.UI.VisjectSurfaceBackground, Width, Height);
|
||||||
|
|
||||||
|
// Draw grid
|
||||||
|
var viewRect = GetClientArea();
|
||||||
|
var upperLeft = _view.PointFromParent(viewRect.Location);
|
||||||
|
var bottomRight = _view.PointFromParent(viewRect.Size);
|
||||||
|
var min = Float2.Min(upperLeft, bottomRight);
|
||||||
|
var max = Float2.Max(upperLeft, bottomRight);
|
||||||
|
var pixelRange = (max - min) * ViewScale;
|
||||||
|
Render2D.PushClip(ref viewRect);
|
||||||
|
DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X);
|
||||||
|
DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y);
|
||||||
|
Render2D.PopClip();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Draw();
|
||||||
|
|
||||||
|
if (!_mouseMovesWidget)
|
||||||
|
{
|
||||||
|
// Clear widgets to collect them during drawing
|
||||||
|
_widgets?.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool drawAnySelectedControl = false;
|
||||||
|
var transformGizmo = TransformGizmo;
|
||||||
|
var mousePos = PointFromWindow(RootWindow.MousePosition);
|
||||||
|
if (transformGizmo != null)
|
||||||
|
{
|
||||||
|
// Selected UI controls outline
|
||||||
|
var selection = transformGizmo.Selection;
|
||||||
|
for (var i = 0; i < selection.Count; i++)
|
||||||
|
{
|
||||||
|
if (IsValidControl(selection[i], out var controlActor))
|
||||||
|
{
|
||||||
|
DrawControl(controlActor, controlActor.Control, true, ref mousePos, ref drawAnySelectedControl, EnableSelecting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
|
||||||
|
if (EnableBackground)
|
||||||
|
{
|
||||||
|
// Draw border
|
||||||
|
if (ContainsFocus)
|
||||||
|
{
|
||||||
|
Render2D.DrawRectangle(new Rectangle(1, 1, Width - 2, Height - 2), Editor.IsPlayMode ? Color.OrangeRed : Style.Current.BackgroundSelected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
|
EndMovingControls();
|
||||||
|
EndMovingView();
|
||||||
|
EndMovingWidget();
|
||||||
|
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Float2 GetControlDelta(Control control, ref Float2 start, ref Float2 end)
|
||||||
|
{
|
||||||
|
var pointOrigin = control.Parent ?? control;
|
||||||
|
var startPos = pointOrigin.PointFromParent(this, start);
|
||||||
|
var endPos = pointOrigin.PointFromParent(this, end);
|
||||||
|
return endPos - startPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange)
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
var linesColor = style.ForegroundDisabled.RGBMultiplied(0.5f);
|
||||||
|
var labelsColor = style.ForegroundDisabled;
|
||||||
|
var labelsSize = 10.0f;
|
||||||
|
Utilities.Utils.DrawCurveTicks((float tick, float strength) =>
|
||||||
|
{
|
||||||
|
var p = _view.PointToParent(axis * tick);
|
||||||
|
|
||||||
|
// Draw line
|
||||||
|
var lineRect = new Rectangle
|
||||||
|
(
|
||||||
|
viewRect.Location + (p - 0.5f) * axis,
|
||||||
|
Float2.Lerp(viewRect.Size, Float2.One, axis)
|
||||||
|
);
|
||||||
|
Render2D.FillRectangle(lineRect, linesColor.AlphaMultiplied(strength));
|
||||||
|
|
||||||
|
// Draw label
|
||||||
|
string label = tick.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
var labelRect = new Rectangle
|
||||||
|
(
|
||||||
|
viewRect.X + 4.0f + (p.X * axis.X),
|
||||||
|
viewRect.Y - labelsSize + (p.Y * axis.Y) + (viewRect.Size.Y * axis.X),
|
||||||
|
50,
|
||||||
|
labelsSize
|
||||||
|
);
|
||||||
|
Render2D.DrawText(style.FontSmall, label, labelRect, labelsColor.AlphaMultiplied(strength), TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||||
|
}, _gridTickSteps, ref _gridTickStrengths, min, max, pixelRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawControl(UIControl uiControl, Control control, bool selection, ref Float2 mousePos, ref bool drawAnySelectedControl, bool withWidgets = false)
|
||||||
|
{
|
||||||
|
if (!drawAnySelectedControl)
|
||||||
|
{
|
||||||
|
drawAnySelectedControl = true;
|
||||||
|
Render2D.PushTransform(ref _cachedTransform);
|
||||||
|
}
|
||||||
|
var options = Editor.Instance.Options.Options.Visual;
|
||||||
|
|
||||||
|
// Draw bounds
|
||||||
|
var bounds = control.EditorBounds;
|
||||||
|
var ul = control.PointToParent(this, bounds.UpperLeft);
|
||||||
|
var ur = control.PointToParent(this, bounds.UpperRight);
|
||||||
|
var bl = control.PointToParent(this, bounds.BottomLeft);
|
||||||
|
var br = control.PointToParent(this, bounds.BottomRight);
|
||||||
|
var color = selection ? options.SelectionOutlineColor0 : Style.Current.SelectionBorder;
|
||||||
|
#if false
|
||||||
|
// AABB
|
||||||
|
var min = Float2.Min(Float2.Min(ul, ur), Float2.Min(bl, br));
|
||||||
|
var max = Float2.Max(Float2.Max(ul, ur), Float2.Max(bl, br));
|
||||||
|
bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero));
|
||||||
|
Render2D.DrawRectangle(bounds, color, options.UISelectionOutlineSize);
|
||||||
|
#else
|
||||||
|
// OBB
|
||||||
|
Render2D.DrawLine(ul, ur, color, options.UISelectionOutlineSize);
|
||||||
|
Render2D.DrawLine(ur, br, color, options.UISelectionOutlineSize);
|
||||||
|
Render2D.DrawLine(br, bl, color, options.UISelectionOutlineSize);
|
||||||
|
Render2D.DrawLine(bl, ul, color, options.UISelectionOutlineSize);
|
||||||
|
#endif
|
||||||
|
if (withWidgets)
|
||||||
|
{
|
||||||
|
// Draw sizing widgets
|
||||||
|
if (_widgets == null)
|
||||||
|
_widgets = new List<Widget>();
|
||||||
|
var widgetSize = 8.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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// TODO: draw anchors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, Float2 resizeAxis, CursorType cursor)
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
var rect = new Rectangle(pos - size * 0.5f, size);
|
||||||
|
if (rect.Contains(ref mousePos))
|
||||||
|
{
|
||||||
|
Render2D.FillRectangle(rect, style.Foreground);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Render2D.FillRectangle(rect, style.ForegroundGrey);
|
||||||
|
Render2D.DrawRectangle(rect, style.Foreground);
|
||||||
|
}
|
||||||
|
if (!_mouseMovesWidget && uiControl != null)
|
||||||
|
{
|
||||||
|
// Collect widget
|
||||||
|
_widgets.Add(new Widget
|
||||||
|
{
|
||||||
|
UIControl = uiControl,
|
||||||
|
Bounds = rect,
|
||||||
|
ResizeAxis = resizeAxis,
|
||||||
|
Cursor = cursor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsValidControl(SceneGraphNode node, out UIControl uiControl)
|
||||||
|
{
|
||||||
|
uiControl = null;
|
||||||
|
if (node.EditableObject is UIControl controlActor)
|
||||||
|
uiControl = controlActor;
|
||||||
|
return uiControl != null &&
|
||||||
|
uiControl.Control != null &&
|
||||||
|
uiControl.Control.VisibleInHierarchy &&
|
||||||
|
uiControl.Control.RootWindow != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RayCastControl(ref Float2 location, out Control hit)
|
||||||
|
{
|
||||||
|
#if false
|
||||||
|
// Raycast only controls with content (eg. skips transparent panels)
|
||||||
|
return RayCastChildren(ref location, out hit);
|
||||||
|
#else
|
||||||
|
// Find any control under mouse (hierarchical)
|
||||||
|
hit = GetChildAtRecursive(location);
|
||||||
|
if (hit is View || hit is CanvasContainer)
|
||||||
|
hit = null;
|
||||||
|
return hit != null;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private UIControlNode FindUIControlNode(Control control)
|
||||||
|
{
|
||||||
|
return FindUIControlNode(TransformGizmo.Owner.SceneGraphRoot, control);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UIControlNode FindUIControlNode(SceneGraphNode node, Control control)
|
||||||
|
{
|
||||||
|
var result = node as UIControlNode;
|
||||||
|
if (result != null && ((UIControl)result.Actor).Control == control)
|
||||||
|
return result;
|
||||||
|
foreach (var e in node.ChildNodes)
|
||||||
|
{
|
||||||
|
result = FindUIControlNode(e, control);
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartUndo()
|
||||||
|
{
|
||||||
|
var undo = TransformGizmo?.Owner?.Undo;
|
||||||
|
if (undo == null || _undoBlock != null)
|
||||||
|
return;
|
||||||
|
_undoBlock = new UndoMultiBlock(undo, TransformGizmo.Selection.ConvertAll(x => x.EditableObject), "Edit control");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndUndo()
|
||||||
|
{
|
||||||
|
if (_undoBlock == null)
|
||||||
|
return;
|
||||||
|
_undoBlock.Dispose();
|
||||||
|
_undoBlock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndMovingControls()
|
||||||
|
{
|
||||||
|
if (!_mouseMovesControl)
|
||||||
|
return;
|
||||||
|
_mouseMovesControl = false;
|
||||||
|
EndMouseCapture();
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
EndUndo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndMovingView()
|
||||||
|
{
|
||||||
|
if (!_mouseMovesView)
|
||||||
|
return;
|
||||||
|
_mouseMovesView = false;
|
||||||
|
EndMouseCapture();
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndMovingWidget()
|
||||||
|
{
|
||||||
|
if (!_mouseMovesWidget)
|
||||||
|
return;
|
||||||
|
_mouseMovesWidget = false;
|
||||||
|
_activeWidget = new Widget();
|
||||||
|
EndMouseCapture();
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
EndUndo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Control that can optionally disable inputs to the children.
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
internal class InputsPassThrough : ContainerControl
|
||||||
|
{
|
||||||
|
private bool _isMouseOver;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if enable input events passing to the UI.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool EnableInputs => true;
|
||||||
|
|
||||||
|
public override bool RayCast(ref Float2 location, out Control hit)
|
||||||
|
{
|
||||||
|
return RayCastChildren(ref location, out hit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ContainsPoint(ref Float2 location, bool precise = false)
|
||||||
|
{
|
||||||
|
if (precise)
|
||||||
|
return false;
|
||||||
|
return base.ContainsPoint(ref location, precise);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnCharInput(char c)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return false;
|
||||||
|
return base.OnCharInput(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return DragDropEffect.None;
|
||||||
|
return base.OnDragDrop(ref location, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return DragDropEffect.None;
|
||||||
|
return base.OnDragEnter(ref location, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDragLeave()
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return;
|
||||||
|
base.OnDragLeave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return DragDropEffect.None;
|
||||||
|
return base.OnDragMove(ref location, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return false;
|
||||||
|
return base.OnKeyDown(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnKeyUp(KeyboardKeys key)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return;
|
||||||
|
base.OnKeyUp(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return false;
|
||||||
|
return base.OnMouseDoubleClick(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return false;
|
||||||
|
return base.OnMouseDown(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsMouseOver => _isMouseOver;
|
||||||
|
|
||||||
|
public override void OnMouseEnter(Float2 location)
|
||||||
|
{
|
||||||
|
_isMouseOver = true;
|
||||||
|
if (!EnableInputs)
|
||||||
|
return;
|
||||||
|
base.OnMouseEnter(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMouseLeave()
|
||||||
|
{
|
||||||
|
_isMouseOver = false;
|
||||||
|
if (!EnableInputs)
|
||||||
|
return;
|
||||||
|
base.OnMouseLeave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMouseMove(Float2 location)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return;
|
||||||
|
base.OnMouseMove(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return false;
|
||||||
|
return base.OnMouseUp(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseWheel(Float2 location, float delta)
|
||||||
|
{
|
||||||
|
if (!EnableInputs)
|
||||||
|
return false;
|
||||||
|
return base.OnMouseWheel(location, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -263,22 +263,22 @@ DEFINE_INTERNAL_CALL(MString*) EditorInternal_GetShaderAssetSourceCode(BinaryAss
|
|||||||
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
|
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
|
||||||
if (obj->WaitForLoaded())
|
if (obj->WaitForLoaded())
|
||||||
DebugLog::ThrowNullReference();
|
DebugLog::ThrowNullReference();
|
||||||
|
|
||||||
auto lock = obj->Storage->Lock();
|
auto lock = obj->Storage->Lock();
|
||||||
|
|
||||||
if (obj->LoadChunk(SHADER_FILE_CHUNK_SOURCE))
|
if (obj->LoadChunk(SHADER_FILE_CHUNK_SOURCE))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
// Decrypt source code
|
||||||
BytesContainer data;
|
BytesContainer data;
|
||||||
obj->GetChunkData(SHADER_FILE_CHUNK_SOURCE, data);
|
obj->GetChunkData(SHADER_FILE_CHUNK_SOURCE, data);
|
||||||
|
auto source = data.Get<char>();
|
||||||
|
auto sourceLength = data.Length();
|
||||||
|
Encryption::DecryptBytes(data.Get(), data.Length());
|
||||||
|
source[sourceLength - 1] = 0;
|
||||||
|
|
||||||
Encryption::DecryptBytes((byte*)data.Get(), data.Length());
|
// Get source and encrypt it back
|
||||||
|
|
||||||
const StringAnsiView srcData((const char*)data.Get(), data.Length());
|
const StringAnsiView srcData((const char*)data.Get(), data.Length());
|
||||||
const String source(srcData);
|
const auto str = MUtils::ToString(srcData);
|
||||||
const auto str = MUtils::ToString(source);
|
Encryption::EncryptBytes(data.Get(), data.Length());
|
||||||
|
|
||||||
Encryption::EncryptBytes((byte*)data.Get(), data.Length());
|
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1106,6 +1106,7 @@ namespace FlaxEditor.Modules
|
|||||||
Proxy.Add(new VisualScriptProxy());
|
Proxy.Add(new VisualScriptProxy());
|
||||||
Proxy.Add(new BehaviorTreeProxy());
|
Proxy.Add(new BehaviorTreeProxy());
|
||||||
Proxy.Add(new LocalizedStringTableProxy());
|
Proxy.Add(new LocalizedStringTableProxy());
|
||||||
|
Proxy.Add(new WidgetProxy());
|
||||||
Proxy.Add(new FileProxy());
|
Proxy.Add(new FileProxy());
|
||||||
Proxy.Add(new SpawnableJsonAssetProxy<PhysicalMaterial>());
|
Proxy.Add(new SpawnableJsonAssetProxy<PhysicalMaterial>());
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,10 @@ namespace FlaxEditor.Modules
|
|||||||
public void Select(List<SceneGraphNode> selection, bool additive = false)
|
public void Select(List<SceneGraphNode> selection, bool additive = false)
|
||||||
{
|
{
|
||||||
if (selection == null)
|
if (selection == null)
|
||||||
throw new ArgumentNullException();
|
{
|
||||||
|
Deselect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Prevent from selecting null nodes
|
// Prevent from selecting null nodes
|
||||||
selection.RemoveAll(x => x == null);
|
selection.RemoveAll(x => x == null);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.IO;
|
|||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.SceneGraph.Actors;
|
using FlaxEditor.SceneGraph.Actors;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Modules
|
namespace FlaxEditor.Modules
|
||||||
@@ -454,6 +455,41 @@ namespace FlaxEditor.Modules
|
|||||||
Profiler.EndEvent();
|
Profiler.EndEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Dictionary<ContainerControl, Float2> _uiRootSizes;
|
||||||
|
|
||||||
|
internal void OnSaveStart(ContainerControl uiRoot)
|
||||||
|
{
|
||||||
|
// Force viewport UI to have fixed size during scene/prefabs saving to result in stable data (less mess in version control diffs)
|
||||||
|
if (_uiRootSizes == null)
|
||||||
|
_uiRootSizes = new Dictionary<ContainerControl, Float2>();
|
||||||
|
_uiRootSizes[uiRoot] = uiRoot.Size;
|
||||||
|
uiRoot.Size = new Float2(1920, 1080);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnSaveEnd(ContainerControl uiRoot)
|
||||||
|
{
|
||||||
|
// Restore cached size of the UI root container
|
||||||
|
if (_uiRootSizes != null && _uiRootSizes.Remove(uiRoot, out var size))
|
||||||
|
{
|
||||||
|
uiRoot.Size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSceneSaving(Scene scene, Guid sceneId)
|
||||||
|
{
|
||||||
|
OnSaveStart(RootControl.GameRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSceneSaved(Scene scene, Guid sceneId)
|
||||||
|
{
|
||||||
|
OnSaveEnd(RootControl.GameRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSceneSaveError(Scene scene, Guid sceneId)
|
||||||
|
{
|
||||||
|
OnSaveEnd(RootControl.GameRoot);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnSceneLoaded(Scene scene, Guid sceneId)
|
private void OnSceneLoaded(Scene scene, Guid sceneId)
|
||||||
{
|
{
|
||||||
var startTime = DateTime.UtcNow;
|
var startTime = DateTime.UtcNow;
|
||||||
@@ -659,6 +695,9 @@ namespace FlaxEditor.Modules
|
|||||||
Root = new ScenesRootNode();
|
Root = new ScenesRootNode();
|
||||||
|
|
||||||
// Bind events
|
// Bind events
|
||||||
|
Level.SceneSaving += OnSceneSaving;
|
||||||
|
Level.SceneSaved += OnSceneSaved;
|
||||||
|
Level.SceneSaveError += OnSceneSaveError;
|
||||||
Level.SceneLoaded += OnSceneLoaded;
|
Level.SceneLoaded += OnSceneLoaded;
|
||||||
Level.SceneUnloading += OnSceneUnloading;
|
Level.SceneUnloading += OnSceneUnloading;
|
||||||
Level.ActorSpawned += OnActorSpawned;
|
Level.ActorSpawned += OnActorSpawned;
|
||||||
@@ -673,6 +712,9 @@ namespace FlaxEditor.Modules
|
|||||||
public override void OnExit()
|
public override void OnExit()
|
||||||
{
|
{
|
||||||
// Unbind events
|
// Unbind events
|
||||||
|
Level.SceneSaving -= OnSceneSaving;
|
||||||
|
Level.SceneSaved -= OnSceneSaved;
|
||||||
|
Level.SceneSaveError -= OnSceneSaveError;
|
||||||
Level.SceneLoaded -= OnSceneLoaded;
|
Level.SceneLoaded -= OnSceneLoaded;
|
||||||
Level.SceneUnloading -= OnSceneUnloading;
|
Level.SceneUnloading -= OnSceneUnloading;
|
||||||
Level.ActorSpawned -= OnActorSpawned;
|
Level.ActorSpawned -= OnActorSpawned;
|
||||||
|
|||||||
@@ -201,8 +201,8 @@ namespace FlaxEditor.Options
|
|||||||
/// <returns>True if input has been processed, otherwise false.</returns>
|
/// <returns>True if input has been processed, otherwise false.</returns>
|
||||||
public bool Process(Control control)
|
public bool Process(Control control)
|
||||||
{
|
{
|
||||||
var root = control.Root;
|
var root = control?.Root;
|
||||||
return root.GetKey(Key) && ProcessModifiers(control);
|
return root != null && root.GetKey(Key) && ProcessModifiers(control);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using FlaxEditor.GUI.Docking;
|
using FlaxEditor.GUI.Docking;
|
||||||
|
using FlaxEditor.Utilities;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.Options
|
namespace FlaxEditor.Options
|
||||||
@@ -116,6 +117,27 @@ namespace FlaxEditor.Options
|
|||||||
BorderlessWindow,
|
BorderlessWindow,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Options for formatting numerical values.
|
||||||
|
/// </summary>
|
||||||
|
public enum ValueFormattingType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No formatting.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format using the base SI unit.
|
||||||
|
/// </summary>
|
||||||
|
BaseUnit,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format using a unit that matches the value best.
|
||||||
|
/// </summary>
|
||||||
|
AutoUnit,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -174,6 +196,20 @@ namespace FlaxEditor.Options
|
|||||||
[EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")]
|
[EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")]
|
||||||
public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal;
|
public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the formatting option for numeric values in the editor.
|
||||||
|
/// </summary>
|
||||||
|
[DefaultValue(ValueFormattingType.None)]
|
||||||
|
[EditorDisplay("Interface"), EditorOrder(300)]
|
||||||
|
public ValueFormattingType ValueFormatting { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the option to put a space between numbers and units for unit formatting.
|
||||||
|
/// </summary>
|
||||||
|
[DefaultValue(false)]
|
||||||
|
[EditorDisplay("Interface"), EditorOrder(310)]
|
||||||
|
public bool SeparateValueAndUnit { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the timestamps prefix mode for output log messages.
|
/// Gets or sets the timestamps prefix mode for output log messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -200,6 +200,27 @@ namespace FlaxEditor.Options
|
|||||||
|
|
||||||
EditorAssets.Cache.OnEditorOptionsChanged(Options);
|
EditorAssets.Cache.OnEditorOptionsChanged(Options);
|
||||||
|
|
||||||
|
// Units formatting options
|
||||||
|
bool useUnitsFormatting = Options.Interface.ValueFormatting != InterfaceOptions.ValueFormattingType.None;
|
||||||
|
bool automaticUnitsFormatting = Options.Interface.ValueFormatting == InterfaceOptions.ValueFormattingType.AutoUnit;
|
||||||
|
bool separateValueAndUnit = Options.Interface.SeparateValueAndUnit;
|
||||||
|
if (useUnitsFormatting != Utilities.Units.UseUnitsFormatting ||
|
||||||
|
automaticUnitsFormatting != Utilities.Units.AutomaticUnitsFormatting ||
|
||||||
|
separateValueAndUnit != Utilities.Units.SeparateValueAndUnit)
|
||||||
|
{
|
||||||
|
Utilities.Units.UseUnitsFormatting = useUnitsFormatting;
|
||||||
|
Utilities.Units.AutomaticUnitsFormatting = automaticUnitsFormatting;
|
||||||
|
Utilities.Units.SeparateValueAndUnit = separateValueAndUnit;
|
||||||
|
|
||||||
|
// Refresh UI in property panels
|
||||||
|
Editor.Windows.PropertiesWin?.Presenter.BuildLayoutOnUpdate();
|
||||||
|
foreach (var window in Editor.Windows.Windows)
|
||||||
|
{
|
||||||
|
if (window is Windows.Assets.PrefabWindow prefabWindow)
|
||||||
|
prefabWindow.Presenter.BuildLayoutOnUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send event
|
// Send event
|
||||||
OptionsChanged?.Invoke(Options);
|
OptionsChanged?.Invoke(Options);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,11 +308,14 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
var selection = Editor.Instance.SceneEditing.Selection;
|
var selection = Editor.Instance.SceneEditing.Selection;
|
||||||
if (selection.Count == 1 && selection[0] is SplinePointNode selectedPoint && selectedPoint.ParentNode == this)
|
if (selection.Count == 1 && selection[0] is SplinePointNode selectedPoint && selectedPoint.ParentNode == this)
|
||||||
{
|
{
|
||||||
if (Input.Keyboard.GetKey(KeyboardKeys.Shift))
|
var mouse = Input.Mouse;
|
||||||
|
var keyboard = Input.Keyboard;
|
||||||
|
|
||||||
|
if (keyboard.GetKey(KeyboardKeys.Shift))
|
||||||
EditSplineWithSnap(selectedPoint);
|
EditSplineWithSnap(selectedPoint);
|
||||||
|
|
||||||
var canAddSplinePoint = Input.Mouse.PositionDelta == Float2.Zero && Input.Mouse.Position != Float2.Zero;
|
var canAddSplinePoint = mouse.PositionDelta == Float2.Zero && mouse.Position != Float2.Zero;
|
||||||
var requestAddSplinePoint = Input.Keyboard.GetKey(KeyboardKeys.Control) && Input.Mouse.GetButtonDown(MouseButton.Right);
|
var requestAddSplinePoint = Input.Keyboard.GetKey(KeyboardKeys.Control) && mouse.GetButtonDown(MouseButton.Right);
|
||||||
if (requestAddSplinePoint && canAddSplinePoint)
|
if (requestAddSplinePoint && canAddSplinePoint)
|
||||||
AddSplinePoint(selectedPoint);
|
AddSplinePoint(selectedPoint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using System.Collections.Generic;
|
|||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
|
using FlaxEditor.Windows.Assets;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.SceneGraph.Actors
|
namespace FlaxEditor.SceneGraph.Actors
|
||||||
@@ -84,76 +85,109 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
{
|
{
|
||||||
base.OnContextMenu(contextMenu, window);
|
base.OnContextMenu(contextMenu, window);
|
||||||
|
|
||||||
contextMenu.AddButton("Add collider", OnAddMeshCollider).Enabled = ((StaticModel)Actor).Model != null;
|
contextMenu.AddButton("Add collider", () => OnAddMeshCollider(window)).Enabled = ((StaticModel)Actor).Model != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAddMeshCollider()
|
private void OnAddMeshCollider(EditorWindow window)
|
||||||
{
|
{
|
||||||
var model = ((StaticModel)Actor).Model;
|
// Allow collider to be added to evey static model selection
|
||||||
if (!model)
|
SceneGraphNode[] selection = Array.Empty<SceneGraphNode>();
|
||||||
return;
|
if (window is SceneTreeWindow)
|
||||||
|
|
||||||
// Special case for in-built Editor models that can use analytical collision
|
|
||||||
var modelPath = model.Path;
|
|
||||||
if (modelPath.EndsWith("/Primitives/Cube.flax", StringComparison.Ordinal))
|
|
||||||
{
|
{
|
||||||
var actor = new BoxCollider
|
selection = Editor.Instance.SceneEditing.Selection.ToArray();
|
||||||
{
|
|
||||||
StaticFlags = Actor.StaticFlags,
|
|
||||||
Transform = Actor.Transform,
|
|
||||||
};
|
|
||||||
Root.Spawn(actor, Actor);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (modelPath.EndsWith("/Primitives/Sphere.flax", StringComparison.Ordinal))
|
else if (window is PrefabWindow prefabWindow)
|
||||||
{
|
{
|
||||||
var actor = new SphereCollider
|
selection = prefabWindow.Selection.ToArray();
|
||||||
{
|
|
||||||
StaticFlags = Actor.StaticFlags,
|
|
||||||
Transform = Actor.Transform,
|
|
||||||
};
|
|
||||||
Root.Spawn(actor, Actor);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (modelPath.EndsWith("/Primitives/Plane.flax", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
var actor = new BoxCollider
|
|
||||||
{
|
|
||||||
StaticFlags = Actor.StaticFlags,
|
|
||||||
Transform = Actor.Transform,
|
|
||||||
Size = new Float3(100.0f, 100.0f, 1.0f),
|
|
||||||
};
|
|
||||||
Root.Spawn(actor, Actor);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (modelPath.EndsWith("/Primitives/Capsule.flax", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
var actor = new CapsuleCollider
|
|
||||||
{
|
|
||||||
StaticFlags = Actor.StaticFlags,
|
|
||||||
Transform = Actor.Transform,
|
|
||||||
Radius = 25.0f,
|
|
||||||
Height = 50.0f,
|
|
||||||
};
|
|
||||||
Editor.Instance.SceneEditing.Spawn(actor, Actor);
|
|
||||||
actor.LocalPosition = new Vector3(0, 50.0f, 0);
|
|
||||||
actor.LocalOrientation = Quaternion.Euler(0, 0, 90.0f);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create collision data (or reuse) and add collision actor
|
var createdNodes = new List<SceneGraphNode>();
|
||||||
Action<CollisionData> created = collisionData =>
|
foreach (var node in selection)
|
||||||
{
|
{
|
||||||
var actor = new MeshCollider
|
if (node is not StaticModelNode staticModelNode)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var model = ((StaticModel)staticModelNode.Actor).Model;
|
||||||
|
if (!model)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Special case for in-built Editor models that can use analytical collision
|
||||||
|
var modelPath = model.Path;
|
||||||
|
if (modelPath.EndsWith("/Primitives/Cube.flax", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
StaticFlags = Actor.StaticFlags,
|
var actor = new BoxCollider
|
||||||
Transform = Actor.Transform,
|
{
|
||||||
CollisionData = collisionData,
|
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||||
|
Transform = staticModelNode.Actor.Transform,
|
||||||
|
};
|
||||||
|
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
|
||||||
|
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (modelPath.EndsWith("/Primitives/Sphere.flax", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var actor = new SphereCollider
|
||||||
|
{
|
||||||
|
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||||
|
Transform = staticModelNode.Actor.Transform,
|
||||||
|
};
|
||||||
|
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
|
||||||
|
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (modelPath.EndsWith("/Primitives/Plane.flax", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var actor = new BoxCollider
|
||||||
|
{
|
||||||
|
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||||
|
Transform = staticModelNode.Actor.Transform,
|
||||||
|
Size = new Float3(100.0f, 100.0f, 1.0f),
|
||||||
|
};
|
||||||
|
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
|
||||||
|
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (modelPath.EndsWith("/Primitives/Capsule.flax", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var actor = new CapsuleCollider
|
||||||
|
{
|
||||||
|
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||||
|
Transform = staticModelNode.Actor.Transform,
|
||||||
|
Radius = 25.0f,
|
||||||
|
Height = 50.0f,
|
||||||
|
};
|
||||||
|
Editor.Instance.SceneEditing.Spawn(actor, staticModelNode.Actor);
|
||||||
|
actor.LocalPosition = new Vector3(0, 50.0f, 0);
|
||||||
|
actor.LocalOrientation = Quaternion.Euler(0, 0, 90.0f);
|
||||||
|
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create collision data (or reuse) and add collision actor
|
||||||
|
Action<CollisionData> created = collisionData =>
|
||||||
|
{
|
||||||
|
var actor = new MeshCollider
|
||||||
|
{
|
||||||
|
StaticFlags = staticModelNode.Actor.StaticFlags,
|
||||||
|
Transform = staticModelNode.Actor.Transform,
|
||||||
|
CollisionData = collisionData,
|
||||||
|
};
|
||||||
|
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
|
||||||
|
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
|
||||||
};
|
};
|
||||||
Root.Spawn(actor, Actor);
|
var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy<CollisionData>();
|
||||||
};
|
collisionDataProxy.CreateCollisionDataFromModel(model, created, selection.Length == 1);
|
||||||
var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy<CollisionData>();
|
}
|
||||||
collisionDataProxy.CreateCollisionDataFromModel(model, created);
|
|
||||||
|
// Select all created nodes
|
||||||
|
if (window is SceneTreeWindow)
|
||||||
|
{
|
||||||
|
Editor.Instance.SceneEditing.Select(createdNodes);
|
||||||
|
}
|
||||||
|
else if (window is PrefabWindow pWindow)
|
||||||
|
{
|
||||||
|
pWindow.Select(createdNodes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -617,8 +617,9 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
public override void SetLocation(int index, Float2 location)
|
public override void SetLocation(int index, Float2 location)
|
||||||
{
|
{
|
||||||
var dataA = (Float4)_node.Values[4 + index * 2];
|
var dataA = (Float4)_node.Values[4 + index * 2];
|
||||||
|
var ranges = (Float4)_node.Values[0];
|
||||||
|
|
||||||
dataA.X = location.X;
|
dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y);
|
||||||
|
|
||||||
_node.Values[4 + index * 2] = dataA;
|
_node.Values[4 + index * 2] = dataA;
|
||||||
_node.Surface.MarkAsEdited();
|
_node.Surface.MarkAsEdited();
|
||||||
@@ -750,9 +751,10 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
public override void SetLocation(int index, Float2 location)
|
public override void SetLocation(int index, Float2 location)
|
||||||
{
|
{
|
||||||
var dataA = (Float4)_node.Values[4 + index * 2];
|
var dataA = (Float4)_node.Values[4 + index * 2];
|
||||||
|
var ranges = (Float4)_node.Values[0];
|
||||||
|
|
||||||
dataA.X = location.X;
|
dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y);
|
||||||
dataA.Y = location.Y;
|
dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W);
|
||||||
|
|
||||||
_node.Values[4 + index * 2] = dataA;
|
_node.Values[4 + index * 2] = dataA;
|
||||||
_node.Surface.MarkAsEdited();
|
_node.Surface.MarkAsEdited();
|
||||||
|
|||||||
@@ -117,17 +117,9 @@ namespace FlaxEditor.Surface
|
|||||||
editor.Panel.Tag = attributeType;
|
editor.Panel.Tag = attributeType;
|
||||||
_presenter = editor;
|
_presenter = editor;
|
||||||
|
|
||||||
using (var stream = new MemoryStream())
|
// Cache 'previous' state to check if attributes were edited after operation
|
||||||
{
|
_oldData = SurfaceMeta.GetAttributesData(attributes);
|
||||||
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
|
|
||||||
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
|
|
||||||
|
|
||||||
var formatter = new BinaryFormatter();
|
|
||||||
#pragma warning disable SYSLIB0011
|
|
||||||
formatter.Serialize(stream, attributes);
|
|
||||||
#pragma warning restore SYSLIB0011
|
|
||||||
_oldData = stream.ToArray();
|
|
||||||
}
|
|
||||||
editor.Select(new Proxy
|
editor.Select(new Proxy
|
||||||
{
|
{
|
||||||
Value = attributes,
|
Value = attributes,
|
||||||
@@ -145,20 +137,11 @@ namespace FlaxEditor.Surface
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
using (var stream = new MemoryStream())
|
|
||||||
{
|
|
||||||
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
|
|
||||||
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
|
|
||||||
|
|
||||||
var formatter = new BinaryFormatter();
|
var newData = SurfaceMeta.GetAttributesData(newValue);
|
||||||
#pragma warning disable SYSLIB0011
|
if (!_oldData.SequenceEqual(newData))
|
||||||
formatter.Serialize(stream, newValue);
|
{
|
||||||
#pragma warning restore SYSLIB0011
|
Edited?.Invoke(newValue);
|
||||||
var newData = stream.ToArray();
|
|
||||||
if (!_oldData.SequenceEqual(newData))
|
|
||||||
{
|
|
||||||
Edited?.Invoke(newValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Hide();
|
Hide();
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.Text;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.Surface
|
namespace FlaxEditor.Surface
|
||||||
@@ -39,28 +39,48 @@ namespace FlaxEditor.Surface
|
|||||||
public readonly List<Entry> Entries = new List<Entry>();
|
public readonly List<Entry> Entries = new List<Entry>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The attribute meta type identifier.
|
/// The attribute meta type identifier. Uses byte[] as storage for Attribute[] serialized with BinaryFormatter (deprecated in .NET 5).
|
||||||
|
/// [Deprecated on 8.12.2023, expires on 8.12.2025]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int AttributeMetaTypeID = 12;
|
public const int OldAttributeMetaTypeID = 12;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The attribute meta type identifier. Uses byte[] as storage for Attribute[] serialized with JsonSerializer.
|
||||||
|
/// </summary>
|
||||||
|
public const int AttributeMetaTypeID = 13;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the attributes collection from the data.
|
/// Gets the attributes collection from the data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">The graph metadata.</param>
|
/// <param name="data">The graph metadata serialized with JsonSerializer.</param>
|
||||||
|
/// <param name="oldData">The graph metadata serialized with BinaryFormatter.</param>
|
||||||
/// <returns>The attributes collection.</returns>
|
/// <returns>The attributes collection.</returns>
|
||||||
public static Attribute[] GetAttributes(byte[] data)
|
public static Attribute[] GetAttributes(byte[] data, byte[] oldData)
|
||||||
{
|
{
|
||||||
if (data != null && data.Length != 0)
|
if (data != null && data.Length != 0)
|
||||||
{
|
{
|
||||||
using (var stream = new MemoryStream(data))
|
try
|
||||||
|
{
|
||||||
|
var json = Encoding.Unicode.GetString(data);
|
||||||
|
return FlaxEngine.Json.JsonSerializer.Deserialize<Attribute[]>(json);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to deserialize Visject attributes array.");
|
||||||
|
Editor.LogWarning(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oldData != null && oldData.Length != 0)
|
||||||
|
{
|
||||||
|
// [Deprecated on 8.12.2023, expires on 8.12.2025]
|
||||||
|
using (var stream = new MemoryStream(oldData))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
|
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
|
||||||
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
|
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
|
||||||
|
|
||||||
var formatter = new BinaryFormatter();
|
|
||||||
#pragma warning disable SYSLIB0011
|
#pragma warning disable SYSLIB0011
|
||||||
|
var formatter = new BinaryFormatter();
|
||||||
return (Attribute[])formatter.Deserialize(stream);
|
return (Attribute[])formatter.Deserialize(stream);
|
||||||
#pragma warning restore SYSLIB0011
|
#pragma warning restore SYSLIB0011
|
||||||
}
|
}
|
||||||
@@ -74,6 +94,21 @@ namespace FlaxEditor.Surface
|
|||||||
return Utils.GetEmptyArray<Attribute>();
|
return Utils.GetEmptyArray<Attribute>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serializes surface attributes into byte[] data using the current format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="attributes">The input attributes.</param>
|
||||||
|
/// <returns>The result array with bytes. Can be empty but not null.</returns>
|
||||||
|
internal static byte[] GetAttributesData(Attribute[] attributes)
|
||||||
|
{
|
||||||
|
if (attributes != null && attributes.Length != 0)
|
||||||
|
{
|
||||||
|
var json = FlaxEngine.Json.JsonSerializer.Serialize(attributes);
|
||||||
|
return Encoding.Unicode.GetBytes(json);
|
||||||
|
}
|
||||||
|
return Utils.GetEmptyArray<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified attribute was defined for this member.
|
/// Determines whether the specified attribute was defined for this member.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -93,7 +128,8 @@ namespace FlaxEditor.Surface
|
|||||||
public static Attribute[] GetAttributes(GraphParameter parameter)
|
public static Attribute[] GetAttributes(GraphParameter parameter)
|
||||||
{
|
{
|
||||||
var data = parameter.GetMetaData(AttributeMetaTypeID);
|
var data = parameter.GetMetaData(AttributeMetaTypeID);
|
||||||
return GetAttributes(data);
|
var dataOld = parameter.GetMetaData(OldAttributeMetaTypeID);
|
||||||
|
return GetAttributes(data, dataOld);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -102,12 +138,7 @@ namespace FlaxEditor.Surface
|
|||||||
/// <returns>The attributes collection.</returns>
|
/// <returns>The attributes collection.</returns>
|
||||||
public Attribute[] GetAttributes()
|
public Attribute[] GetAttributes()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Entries.Count; i++)
|
return GetAttributes(GetEntry(AttributeMetaTypeID).Data, GetEntry(OldAttributeMetaTypeID).Data);
|
||||||
{
|
|
||||||
if (Entries[i].TypeID == AttributeMetaTypeID)
|
|
||||||
return GetAttributes(Entries[i].Data);
|
|
||||||
}
|
|
||||||
return Utils.GetEmptyArray<Attribute>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -119,25 +150,12 @@ namespace FlaxEditor.Surface
|
|||||||
if (attributes == null || attributes.Length == 0)
|
if (attributes == null || attributes.Length == 0)
|
||||||
{
|
{
|
||||||
RemoveEntry(AttributeMetaTypeID);
|
RemoveEntry(AttributeMetaTypeID);
|
||||||
|
RemoveEntry(OldAttributeMetaTypeID);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < attributes.Length; i++)
|
AddEntry(AttributeMetaTypeID, GetAttributesData(attributes));
|
||||||
{
|
RemoveEntry(OldAttributeMetaTypeID);
|
||||||
if (attributes[i] == null)
|
|
||||||
throw new NullReferenceException("One of the Visject attributes is null.");
|
|
||||||
}
|
|
||||||
using (var stream = new MemoryStream())
|
|
||||||
{
|
|
||||||
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
|
|
||||||
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
|
|
||||||
|
|
||||||
var formatter = new BinaryFormatter();
|
|
||||||
#pragma warning disable SYSLIB0011
|
|
||||||
formatter.Serialize(stream, attributes);
|
|
||||||
#pragma warning restore SYSLIB0011
|
|
||||||
AddEntry(AttributeMetaTypeID, stream.ToArray());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,11 +198,11 @@ namespace FlaxEditor.Surface
|
|||||||
/// <returns>True if cannot save data</returns>
|
/// <returns>True if cannot save data</returns>
|
||||||
public void Save(BinaryWriter stream)
|
public void Save(BinaryWriter stream)
|
||||||
{
|
{
|
||||||
stream.Write(Entries.Count);
|
var entries = Entries;
|
||||||
|
stream.Write(entries.Count);
|
||||||
for (int i = 0; i < Entries.Count; i++)
|
for (int i = 0; i < entries.Count; i++)
|
||||||
{
|
{
|
||||||
Entry e = Entries[i];
|
Entry e = entries[i];
|
||||||
|
|
||||||
stream.Write(e.TypeID);
|
stream.Write(e.TypeID);
|
||||||
stream.Write((long)0);
|
stream.Write((long)0);
|
||||||
@@ -214,14 +232,12 @@ namespace FlaxEditor.Surface
|
|||||||
/// <returns>Entry</returns>
|
/// <returns>Entry</returns>
|
||||||
public Entry GetEntry(int typeID)
|
public Entry GetEntry(int typeID)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Entries.Count; i++)
|
var entries = Entries;
|
||||||
|
for (int i = 0; i < entries.Count; i++)
|
||||||
{
|
{
|
||||||
if (Entries[i].TypeID == typeID)
|
if (entries[i].TypeID == typeID)
|
||||||
{
|
return entries[i];
|
||||||
return Entries[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Entry();
|
return new Entry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,11 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void DrawBackground()
|
protected virtual void DrawBackground()
|
||||||
{
|
{
|
||||||
var background = Style.Background;
|
DrawBackgroundDefault(Style.Background, Width, Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void DrawBackgroundDefault(Texture background, float width, float height)
|
||||||
|
{
|
||||||
if (background && background.ResidentMipLevels > 0)
|
if (background && background.ResidentMipLevels > 0)
|
||||||
{
|
{
|
||||||
var bSize = background.Size;
|
var bSize = background.Size;
|
||||||
@@ -77,8 +81,8 @@ namespace FlaxEditor.Surface
|
|||||||
if (pos.Y > 0)
|
if (pos.Y > 0)
|
||||||
pos.Y -= bh;
|
pos.Y -= bh;
|
||||||
|
|
||||||
int maxI = Mathf.CeilToInt(Width / bw + 1.0f);
|
int maxI = Mathf.CeilToInt(width / bw + 1.0f);
|
||||||
int maxJ = Mathf.CeilToInt(Height / bh + 1.0f);
|
int maxJ = Mathf.CeilToInt(height / bh + 1.0f);
|
||||||
|
|
||||||
for (int i = 0; i < maxI; i++)
|
for (int i = 0; i < maxI; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||||
#include "Engine/Core/Config/GameSettings.h"
|
#include "Engine/Core/Config/GameSettings.h"
|
||||||
|
#include "Engine/Core/Config/BuildSettings.h"
|
||||||
#include "Engine/Content/Content.h"
|
#include "Engine/Content/Content.h"
|
||||||
#include "Engine/Content/AssetReference.h"
|
#include "Engine/Content/AssetReference.h"
|
||||||
#include "Engine/Content/Assets/Texture.h"
|
#include "Engine/Content/Assets/Texture.h"
|
||||||
@@ -17,7 +18,18 @@
|
|||||||
#if PLATFORM_MAC
|
#if PLATFORM_MAC
|
||||||
#include "Engine/Platform/Apple/ApplePlatformSettings.h"
|
#include "Engine/Platform/Apple/ApplePlatformSettings.h"
|
||||||
#endif
|
#endif
|
||||||
#include <fstream>
|
|
||||||
|
String EditorUtilities::GetOutputName()
|
||||||
|
{
|
||||||
|
const auto gameSettings = GameSettings::Get();
|
||||||
|
const auto buildSettings = BuildSettings::Get();
|
||||||
|
String outputName = buildSettings->OutputName;
|
||||||
|
outputName.Replace(TEXT("${PROJECT_NAME}"), *gameSettings->ProductName, StringSearchCase::IgnoreCase);
|
||||||
|
outputName.Replace(TEXT("${COMPANY_NAME}"), *gameSettings->CompanyName, StringSearchCase::IgnoreCase);
|
||||||
|
if (outputName.IsEmpty())
|
||||||
|
outputName = TEXT("FlaxGame");
|
||||||
|
return outputName;
|
||||||
|
}
|
||||||
|
|
||||||
bool EditorUtilities::FormatAppPackageName(String& packageName)
|
bool EditorUtilities::FormatAppPackageName(String& packageName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public:
|
|||||||
SplashScreen,
|
SplashScreen,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static String GetOutputName();
|
||||||
static bool FormatAppPackageName(String& packageName);
|
static bool FormatAppPackageName(String& packageName);
|
||||||
static bool GetApplicationImage(const Guid& imageId, TextureData& imageData, ApplicationImageType type = ApplicationImageType::Icon);
|
static bool GetApplicationImage(const Guid& imageId, TextureData& imageData, ApplicationImageType type = ApplicationImageType::Icon);
|
||||||
static bool GetTexture(const Guid& textureId, TextureData& textureData);
|
static bool GetTexture(const Guid& textureId, TextureData& textureData);
|
||||||
|
|||||||
@@ -121,6 +121,37 @@ namespace FlaxEditor.Utilities
|
|||||||
["e"] = Math.E,
|
["e"] = Math.E,
|
||||||
["infinity"] = double.MaxValue,
|
["infinity"] = double.MaxValue,
|
||||||
["-infinity"] = -double.MaxValue,
|
["-infinity"] = -double.MaxValue,
|
||||||
|
["m"] = Units.Meters2Units,
|
||||||
|
["cm"] = Units.Meters2Units / 100,
|
||||||
|
["km"] = Units.Meters2Units * 1000,
|
||||||
|
["s"] = 1,
|
||||||
|
["ms"] = 0.001,
|
||||||
|
["min"] = 60,
|
||||||
|
["h"] = 3600,
|
||||||
|
["cm²"] = (Units.Meters2Units / 100) * (Units.Meters2Units / 100),
|
||||||
|
["cm³"] = (Units.Meters2Units / 100) * (Units.Meters2Units / 100) * (Units.Meters2Units / 100),
|
||||||
|
["dm²"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10),
|
||||||
|
["dm³"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10) * (Units.Meters2Units / 10),
|
||||||
|
["l"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10) * (Units.Meters2Units / 10),
|
||||||
|
["m²"] = Units.Meters2Units * Units.Meters2Units,
|
||||||
|
["m³"] = Units.Meters2Units * Units.Meters2Units * Units.Meters2Units,
|
||||||
|
["kg"] = 1,
|
||||||
|
["g"] = 0.001,
|
||||||
|
["n"] = Units.Meters2Units
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List known units which cannot be handled as a variable easily because they contain operator symbols (mostly a forward slash). The value is the factor to calculate game units.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly IDictionary<string, double> UnitSymbols = new Dictionary<string, double>
|
||||||
|
{
|
||||||
|
["cm/s"] = Units.Meters2Units / 100,
|
||||||
|
["cm/s²"] = Units.Meters2Units / 100,
|
||||||
|
["m/s"] = Units.Meters2Units,
|
||||||
|
["m/s²"] = Units.Meters2Units,
|
||||||
|
["km/h"] = 1 / 3.6 * Units.Meters2Units,
|
||||||
|
// Nm is here because these values are compared case-sensitive, and we don't want to confuse nanometers and Newtonmeters
|
||||||
|
["Nm"] = Units.Meters2Units * Units.Meters2Units,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -156,7 +187,7 @@ namespace FlaxEditor.Utilities
|
|||||||
if (Operators.ContainsKey(str))
|
if (Operators.ContainsKey(str))
|
||||||
return TokenType.Operator;
|
return TokenType.Operator;
|
||||||
|
|
||||||
if (char.IsLetter(c))
|
if (char.IsLetter(c) || c == '²' || c == '³')
|
||||||
return TokenType.Variable;
|
return TokenType.Variable;
|
||||||
|
|
||||||
throw new ParsingException("wrong character");
|
throw new ParsingException("wrong character");
|
||||||
@@ -170,7 +201,24 @@ namespace FlaxEditor.Utilities
|
|||||||
public static IEnumerable<Token> Tokenize(string text)
|
public static IEnumerable<Token> Tokenize(string text)
|
||||||
{
|
{
|
||||||
// Prepare text
|
// Prepare text
|
||||||
text = text.Replace(',', '.');
|
text = text.Replace(',', '.').Replace("°", "");
|
||||||
|
foreach (var kv in UnitSymbols)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
idx = text.IndexOf(kv.Key, StringComparison.InvariantCulture);
|
||||||
|
if (idx > 0)
|
||||||
|
{
|
||||||
|
if (DetermineType(text[idx - 1]) != TokenType.Number)
|
||||||
|
throw new ParsingException($"unit found without a number: {kv.Key} at {idx} in {text}");
|
||||||
|
if (Mathf.Abs(kv.Value - 1) < Mathf.Epsilon)
|
||||||
|
text = text.Remove(idx, kv.Key.Length);
|
||||||
|
else
|
||||||
|
text = text.Replace(kv.Key, "*" + kv.Value);
|
||||||
|
}
|
||||||
|
} while (idx > 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Necessary to correctly parse negative numbers
|
// Necessary to correctly parse negative numbers
|
||||||
var previous = TokenType.WhiteSpace;
|
var previous = TokenType.WhiteSpace;
|
||||||
@@ -240,6 +288,11 @@ namespace FlaxEditor.Utilities
|
|||||||
}
|
}
|
||||||
else if (type == TokenType.Variable)
|
else if (type == TokenType.Variable)
|
||||||
{
|
{
|
||||||
|
if (previous == TokenType.Number)
|
||||||
|
{
|
||||||
|
previous = TokenType.Operator;
|
||||||
|
yield return new Token(TokenType.Operator, "*");
|
||||||
|
}
|
||||||
// Continue till the end of the variable
|
// Continue till the end of the variable
|
||||||
while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Variable)
|
while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Variable)
|
||||||
{
|
{
|
||||||
@@ -335,7 +388,7 @@ namespace FlaxEditor.Utilities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ParsingException("unknown variable");
|
throw new ParsingException($"unknown variable : {token.Value}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -372,6 +425,15 @@ namespace FlaxEditor.Utilities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if stack has more than one item we're not finished with evaluating
|
||||||
|
// we assume the remaining values are all factors to be multiplied
|
||||||
|
if (stack.Count > 1)
|
||||||
|
{
|
||||||
|
var v1 = stack.Pop();
|
||||||
|
while (stack.Count > 0)
|
||||||
|
v1 *= stack.Pop();
|
||||||
|
return v1;
|
||||||
|
}
|
||||||
return stack.Pop();
|
return stack.Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
41
Source/Editor/Utilities/Units.cs
Normal file
41
Source/Editor/Utilities/Units.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
namespace FlaxEditor.Utilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Units display utilities for Editor.
|
||||||
|
/// </summary>
|
||||||
|
public class Units
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Factor of units per meter.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly float Meters2Units = 100f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// False to always show game units without any postfix.
|
||||||
|
/// </summary>
|
||||||
|
public static bool UseUnitsFormatting = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a space between numbers and units for readability.
|
||||||
|
/// </summary>
|
||||||
|
public static bool SeparateValueAndUnit = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters.
|
||||||
|
/// </summary>
|
||||||
|
public static bool AutomaticUnitsFormatting = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the unit according to user settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="unit">The unit name.</param>
|
||||||
|
/// <returns>The formatted text.</returns>
|
||||||
|
public static string Unit(string unit)
|
||||||
|
{
|
||||||
|
if (SeparateValueAndUnit)
|
||||||
|
return $" {unit}";
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -243,6 +243,63 @@ namespace FlaxEditor.Utilities
|
|||||||
500000, 1000000, 5000000, 10000000, 100000000
|
500000, 1000000, 5000000, 10000000, 100000000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
internal delegate void DrawCurveTick(float tick, float strength);
|
||||||
|
|
||||||
|
internal static Int2 DrawCurveTicks(DrawCurveTick drawTick, float[] tickSteps, ref float[] tickStrengths, float min, float max, float pixelRange, float minDistanceBetweenTicks = 20, float maxDistanceBetweenTicks = 60)
|
||||||
|
{
|
||||||
|
if (tickStrengths == null || tickStrengths.Length != tickSteps.Length)
|
||||||
|
tickStrengths = new float[tickSteps.Length];
|
||||||
|
|
||||||
|
// Find the strength for each modulo number tick marker
|
||||||
|
var pixelsInRange = pixelRange / (max - min);
|
||||||
|
var smallestTick = 0;
|
||||||
|
var biggestTick = tickSteps.Length - 1;
|
||||||
|
for (int i = tickSteps.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
// Calculate how far apart these modulo tick steps are spaced
|
||||||
|
float tickSpacing = tickSteps[i] * pixelsInRange;
|
||||||
|
|
||||||
|
// Calculate the strength of the tick markers based on the spacing
|
||||||
|
tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks));
|
||||||
|
|
||||||
|
// Beyond threshold the ticks don't get any bigger or fatter
|
||||||
|
if (tickStrengths[i] >= 1)
|
||||||
|
biggestTick = i;
|
||||||
|
|
||||||
|
// Do not show small tick markers
|
||||||
|
if (tickSpacing <= minDistanceBetweenTicks)
|
||||||
|
{
|
||||||
|
smallestTick = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tickLevels = biggestTick - smallestTick + 1;
|
||||||
|
|
||||||
|
// Draw all tick levels
|
||||||
|
for (int level = 0; level < tickLevels; level++)
|
||||||
|
{
|
||||||
|
float strength = tickStrengths[smallestTick + level];
|
||||||
|
if (strength <= Mathf.Epsilon)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Draw all ticks
|
||||||
|
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 1);
|
||||||
|
var lStep = tickSteps[l];
|
||||||
|
var lNextStep = tickSteps[l + 1];
|
||||||
|
int startTick = Mathf.FloorToInt(min / lStep);
|
||||||
|
int endTick = Mathf.CeilToInt(max / lStep);
|
||||||
|
for (int i = startTick; i <= endTick; i++)
|
||||||
|
{
|
||||||
|
if (l < biggestTick && (i % Mathf.RoundToInt(lNextStep / lStep) == 0))
|
||||||
|
continue;
|
||||||
|
var tick = i * lStep;
|
||||||
|
drawTick(tick, strength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Int2(smallestTick, biggestTick);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified path string contains any invalid character.
|
/// Determines whether the specified path string contains any invalid character.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1187,6 +1244,71 @@ namespace FlaxEditor.Utilities
|
|||||||
return StringUtils.GetPathWithoutExtension(path);
|
return StringUtils.GetPathWithoutExtension(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string InternalFormat(double value, string format, FlaxEngine.Utils.ValueCategory category)
|
||||||
|
{
|
||||||
|
switch (category)
|
||||||
|
{
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Distance:
|
||||||
|
if (!Units.AutomaticUnitsFormatting)
|
||||||
|
return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m");
|
||||||
|
var absValue = Mathf.Abs(value);
|
||||||
|
// in case a unit != cm this would be (value / Meters2Units * 100)
|
||||||
|
if (absValue < Units.Meters2Units)
|
||||||
|
return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("cm");
|
||||||
|
if (absValue < Units.Meters2Units * 1000)
|
||||||
|
return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m");
|
||||||
|
return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("km");
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°";
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("s");
|
||||||
|
// some fonts have a symbol for that: "\u33A7"
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s");
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s²");
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m²");
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m³");
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("kg");
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("N");
|
||||||
|
case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("Nm");
|
||||||
|
case FlaxEngine.Utils.ValueCategory.None:
|
||||||
|
default: return FormatFloat(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format a float value either as-is, with a distance unit or with a degree sign.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to format.</param>
|
||||||
|
/// <param name="category">The value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign.</param>
|
||||||
|
/// <returns>The formatted string.</returns>
|
||||||
|
public static string FormatFloat(float value, FlaxEngine.Utils.ValueCategory category)
|
||||||
|
{
|
||||||
|
if (float.IsPositiveInfinity(value) || value == float.MaxValue)
|
||||||
|
return "Infinity";
|
||||||
|
if (float.IsNegativeInfinity(value) || value == float.MinValue)
|
||||||
|
return "-Infinity";
|
||||||
|
if (!Units.UseUnitsFormatting || category == FlaxEngine.Utils.ValueCategory.None)
|
||||||
|
return FormatFloat(value);
|
||||||
|
const string format = "G7";
|
||||||
|
return InternalFormat(value, format, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format a double value either as-is, with a distance unit or with a degree sign
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to format.</param>
|
||||||
|
/// <param name="category">The value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign.</param>
|
||||||
|
/// <returns>The formatted string.</returns>
|
||||||
|
public static string FormatFloat(double value, FlaxEngine.Utils.ValueCategory category)
|
||||||
|
{
|
||||||
|
if (double.IsPositiveInfinity(value) || value == double.MaxValue)
|
||||||
|
return "Infinity";
|
||||||
|
if (double.IsNegativeInfinity(value) || value == double.MinValue)
|
||||||
|
return "-Infinity";
|
||||||
|
if (!Units.UseUnitsFormatting || category == FlaxEngine.Utils.ValueCategory.None)
|
||||||
|
return FormatFloat(value);
|
||||||
|
const string format = "G15";
|
||||||
|
return InternalFormat(value, format, category);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Formats the floating point value (double precision) into the readable text representation.
|
/// Formats the floating point value (double precision) into the readable text representation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1198,7 +1320,7 @@ namespace FlaxEditor.Utilities
|
|||||||
return "Infinity";
|
return "Infinity";
|
||||||
if (float.IsNegativeInfinity(value) || value == float.MinValue)
|
if (float.IsNegativeInfinity(value) || value == float.MinValue)
|
||||||
return "-Infinity";
|
return "-Infinity";
|
||||||
string str = value.ToString("r", CultureInfo.InvariantCulture);
|
string str = value.ToString("R", CultureInfo.InvariantCulture);
|
||||||
return FormatFloat(str, value < 0);
|
return FormatFloat(str, value < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1213,7 +1335,7 @@ namespace FlaxEditor.Utilities
|
|||||||
return "Infinity";
|
return "Infinity";
|
||||||
if (double.IsNegativeInfinity(value) || value == double.MinValue)
|
if (double.IsNegativeInfinity(value) || value == double.MinValue)
|
||||||
return "-Infinity";
|
return "-Infinity";
|
||||||
string str = value.ToString("r", CultureInfo.InvariantCulture);
|
string str = value.ToString("R", CultureInfo.InvariantCulture);
|
||||||
return FormatFloat(str, value < 0);
|
return FormatFloat(str, value < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,8 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
|
|||||||
|
|
||||||
void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw)
|
void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw)
|
||||||
{
|
{
|
||||||
|
if (!actor || !actor->IsActiveInHierarchy())
|
||||||
|
return;
|
||||||
auto& view = renderContext.View;
|
auto& view = renderContext.View;
|
||||||
const BoundingFrustum frustum = view.Frustum;
|
const BoundingFrustum frustum = view.Frustum;
|
||||||
Matrix m1, m2, world;
|
Matrix m1, m2, world;
|
||||||
@@ -208,8 +210,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
|
|||||||
draw.DrawState = &drawState;
|
draw.DrawState = &drawState;
|
||||||
draw.Deformation = nullptr;
|
draw.Deformation = nullptr;
|
||||||
|
|
||||||
// Support custom icons through types, but not onces that were added through actors,
|
// Support custom icons through types, but not ones that were added through actors, since they cant register while in prefab view anyway
|
||||||
// since they cant register while in prefab view anyway
|
|
||||||
if (ActorTypeToTexture.TryGet(actor->GetTypeHandle(), texture))
|
if (ActorTypeToTexture.TryGet(actor->GetTypeHandle(), texture))
|
||||||
{
|
{
|
||||||
// Use custom texture
|
// Use custom texture
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ using Real = System.Double;
|
|||||||
using Real = System.Single;
|
using Real = System.Single;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using FlaxEditor.Gizmo;
|
using FlaxEditor.Gizmo;
|
||||||
using FlaxEditor.SceneGraph;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Cameras
|
namespace FlaxEditor.Viewport.Cameras
|
||||||
@@ -85,86 +83,8 @@ namespace FlaxEditor.Viewport.Cameras
|
|||||||
_moveStartTime = Time.UnscaledGameTime;
|
_moveStartTime = Time.UnscaledGameTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Moves the viewport to visualize the actor.
|
public override void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation)
|
||||||
/// </summary>
|
|
||||||
/// <param name="actor">The actor to preview.</param>
|
|
||||||
public void ShowActor(Actor actor)
|
|
||||||
{
|
|
||||||
Editor.GetActorEditorSphere(actor, out BoundingSphere sphere);
|
|
||||||
ShowSphere(ref sphere);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the viewport to visualize selected actors.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="actor">The actors to show.</param>
|
|
||||||
/// <param name="orientation">The used orientation.</param>
|
|
||||||
public void ShowActor(Actor actor, ref Quaternion orientation)
|
|
||||||
{
|
|
||||||
Editor.GetActorEditorSphere(actor, out BoundingSphere sphere);
|
|
||||||
ShowSphere(ref sphere, ref orientation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the viewport to visualize selected actors.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="selection">The actors to show.</param>
|
|
||||||
public void ShowActors(List<SceneGraphNode> selection)
|
|
||||||
{
|
|
||||||
if (selection.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
BoundingSphere mergesSphere = BoundingSphere.Empty;
|
|
||||||
for (int i = 0; i < selection.Count; i++)
|
|
||||||
{
|
|
||||||
selection[i].GetEditorSphere(out var sphere);
|
|
||||||
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mergesSphere == BoundingSphere.Empty)
|
|
||||||
return;
|
|
||||||
ShowSphere(ref mergesSphere);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the viewport to visualize selected actors.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="selection">The actors to show.</param>
|
|
||||||
/// <param name="orientation">The used orientation.</param>
|
|
||||||
public void ShowActors(List<SceneGraphNode> selection, ref Quaternion orientation)
|
|
||||||
{
|
|
||||||
if (selection.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
BoundingSphere mergesSphere = BoundingSphere.Empty;
|
|
||||||
for (int i = 0; i < selection.Count; i++)
|
|
||||||
{
|
|
||||||
selection[i].GetEditorSphere(out var sphere);
|
|
||||||
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mergesSphere == BoundingSphere.Empty)
|
|
||||||
return;
|
|
||||||
ShowSphere(ref mergesSphere, ref orientation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the camera to visualize given world area defined by the sphere.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sphere">The sphere.</param>
|
|
||||||
public void ShowSphere(ref BoundingSphere sphere)
|
|
||||||
{
|
|
||||||
var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f);
|
|
||||||
ShowSphere(ref sphere, ref q);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the camera to visualize given world area defined by the sphere.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sphere">The sphere.</param>
|
|
||||||
/// <param name="orientation">The camera orientation.</param>
|
|
||||||
public void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation)
|
|
||||||
{
|
{
|
||||||
Vector3 position;
|
Vector3 position;
|
||||||
if (Viewport.UseOrthographicProjection)
|
if (Viewport.UseOrthographicProjection)
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ using Real = System.Double;
|
|||||||
using Real = System.Single;
|
using Real = System.Single;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FlaxEditor.Gizmo;
|
||||||
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Cameras
|
namespace FlaxEditor.Viewport.Cameras
|
||||||
@@ -33,6 +36,90 @@ namespace FlaxEditor.Viewport.Cameras
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool UseMovementSpeed => true;
|
public virtual bool UseMovementSpeed => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Focuses the viewport on the current selection of the gizmo.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gizmos">The gizmo collection (from viewport).</param>
|
||||||
|
/// <param name="orientation">The target view orientation.</param>
|
||||||
|
public virtual void FocusSelection(GizmosCollection gizmos, ref Quaternion orientation)
|
||||||
|
{
|
||||||
|
var transformGizmo = gizmos.Get<TransformGizmo>();
|
||||||
|
if (transformGizmo == null || transformGizmo.SelectedParents.Count == 0)
|
||||||
|
return;
|
||||||
|
if (gizmos.Active != null)
|
||||||
|
{
|
||||||
|
var gizmoBounds = gizmos.Active.FocusBounds;
|
||||||
|
if (gizmoBounds != BoundingSphere.Empty)
|
||||||
|
{
|
||||||
|
ShowSphere(ref gizmoBounds, ref orientation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ShowActors(transformGizmo.SelectedParents, ref orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the viewport to visualize the actor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="actor">The actor to preview.</param>
|
||||||
|
public virtual void ShowActor(Actor actor)
|
||||||
|
{
|
||||||
|
Editor.GetActorEditorSphere(actor, out BoundingSphere sphere);
|
||||||
|
ShowSphere(ref sphere);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the viewport to visualize selected actors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="selection">The actors to show.</param>
|
||||||
|
public void ShowActors(List<SceneGraphNode> selection)
|
||||||
|
{
|
||||||
|
var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f);
|
||||||
|
ShowActors(selection, ref q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the viewport to visualize selected actors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="selection">The actors to show.</param>
|
||||||
|
/// <param name="orientation">The used orientation.</param>
|
||||||
|
public virtual void ShowActors(List<SceneGraphNode> selection, ref Quaternion orientation)
|
||||||
|
{
|
||||||
|
if (selection.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BoundingSphere mergesSphere = BoundingSphere.Empty;
|
||||||
|
for (int i = 0; i < selection.Count; i++)
|
||||||
|
{
|
||||||
|
selection[i].GetEditorSphere(out var sphere);
|
||||||
|
BoundingSphere.Merge(ref mergesSphere, ref sphere, out mergesSphere);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mergesSphere == BoundingSphere.Empty)
|
||||||
|
return;
|
||||||
|
ShowSphere(ref mergesSphere, ref orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the camera to visualize given world area defined by the sphere.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sphere">The sphere.</param>
|
||||||
|
public void ShowSphere(ref BoundingSphere sphere)
|
||||||
|
{
|
||||||
|
var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f);
|
||||||
|
ShowSphere(ref sphere, ref q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the camera to visualize given world area defined by the sphere.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sphere">The sphere.</param>
|
||||||
|
/// <param name="orientation">The camera orientation.</param>
|
||||||
|
public virtual void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation)
|
||||||
|
{
|
||||||
|
SetArcBallView(orientation, sphere.Center, sphere.Radius);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets view orientation and position to match the arc ball camera style view for the given target object bounds.
|
/// Sets view orientation and position to match the arc ball camera style view for the given target object bounds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using FlaxEditor.Gizmo;
|
using FlaxEditor.Gizmo;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.Viewport.Cameras;
|
using FlaxEditor.Viewport.Cameras;
|
||||||
|
using FlaxEditor.Viewport.Widgets;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -41,6 +44,7 @@ namespace FlaxEditor.Viewport
|
|||||||
Gizmos[i].Update(deltaTime);
|
Gizmos[i].Update(deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public EditorViewport Viewport => this;
|
public EditorViewport Viewport => this;
|
||||||
|
|
||||||
@@ -66,19 +70,19 @@ namespace FlaxEditor.Viewport
|
|||||||
public bool IsControlDown => _input.IsControlDown;
|
public bool IsControlDown => _input.IsControlDown;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool SnapToGround => Editor.Instance.Options.Options.Input.SnapToGround.Process(Root);
|
public bool SnapToGround => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToGround.Process(Root);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool SnapToVertex => Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Float2 MouseDelta => _mouseDelta * 1000;
|
public Float2 MouseDelta => _mouseDelta * 1000;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool UseSnapping => Root.GetKey(KeyboardKeys.Control);
|
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool UseDuplicate => Root.GetKey(KeyboardKeys.Shift);
|
public bool UseDuplicate => Root?.GetKey(KeyboardKeys.Shift) ?? false;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Undo Undo { get; }
|
public Undo Undo { get; }
|
||||||
@@ -92,6 +96,9 @@ namespace FlaxEditor.Viewport
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public abstract void Spawn(Actor actor);
|
public abstract void Spawn(Actor actor);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract void OpenContextMenu();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
|
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
|
||||||
|
|
||||||
@@ -121,5 +128,284 @@ namespace FlaxEditor.Viewport
|
|||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void AddGizmoViewportWidgets(EditorViewport viewport, TransformGizmo transformGizmo, bool useProjectCache = false)
|
||||||
|
{
|
||||||
|
var editor = Editor.Instance;
|
||||||
|
var inputOptions = editor.Options.Options.Input;
|
||||||
|
|
||||||
|
if (useProjectCache)
|
||||||
|
{
|
||||||
|
// Initialize snapping enabled from cached values
|
||||||
|
if (editor.ProjectCache.TryGetCustomData("TranslateSnapState", out var cachedState))
|
||||||
|
transformGizmo.TranslationSnapEnable = bool.Parse(cachedState);
|
||||||
|
if (editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedState))
|
||||||
|
transformGizmo.RotationSnapEnabled = bool.Parse(cachedState);
|
||||||
|
if (editor.ProjectCache.TryGetCustomData("ScaleSnapState", out cachedState))
|
||||||
|
transformGizmo.ScaleSnapEnabled = bool.Parse(cachedState);
|
||||||
|
if (editor.ProjectCache.TryGetCustomData("TranslateSnapValue", out cachedState))
|
||||||
|
transformGizmo.TranslationSnapValue = float.Parse(cachedState);
|
||||||
|
if (editor.ProjectCache.TryGetCustomData("RotationSnapValue", out cachedState))
|
||||||
|
transformGizmo.RotationSnapValue = float.Parse(cachedState);
|
||||||
|
if (editor.ProjectCache.TryGetCustomData("ScaleSnapValue", out cachedState))
|
||||||
|
transformGizmo.ScaleSnapValue = float.Parse(cachedState);
|
||||||
|
if (editor.ProjectCache.TryGetCustomData("TransformSpaceState", out cachedState) && Enum.TryParse(cachedState, out TransformGizmoBase.TransformSpace space))
|
||||||
|
transformGizmo.ActiveTransformSpace = space;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform space widget
|
||||||
|
var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true)
|
||||||
|
{
|
||||||
|
Checked = transformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World,
|
||||||
|
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
|
||||||
|
Parent = transformSpaceWidget
|
||||||
|
};
|
||||||
|
transformSpaceToggle.Toggled += _ =>
|
||||||
|
{
|
||||||
|
transformGizmo.ToggleTransformSpace();
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString());
|
||||||
|
};
|
||||||
|
transformSpaceWidget.Parent = viewport;
|
||||||
|
|
||||||
|
// Scale snapping widget
|
||||||
|
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true)
|
||||||
|
{
|
||||||
|
Checked = transformGizmo.ScaleSnapEnabled,
|
||||||
|
TooltipText = "Enable scale snapping",
|
||||||
|
Parent = scaleSnappingWidget
|
||||||
|
};
|
||||||
|
enableScaleSnapping.Toggled += _ =>
|
||||||
|
{
|
||||||
|
transformGizmo.ScaleSnapEnabled = !transformGizmo.ScaleSnapEnabled;
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("ScaleSnapState", transformGizmo.ScaleSnapEnabled.ToString());
|
||||||
|
};
|
||||||
|
var scaleSnappingCM = new ContextMenu();
|
||||||
|
var scaleSnapping = new ViewportWidgetButton(transformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM)
|
||||||
|
{
|
||||||
|
TooltipText = "Scale snapping values"
|
||||||
|
};
|
||||||
|
for (int i = 0; i < ScaleSnapValues.Length; i++)
|
||||||
|
{
|
||||||
|
var v = ScaleSnapValues[i];
|
||||||
|
var button = scaleSnappingCM.AddButton(v.ToString());
|
||||||
|
button.Tag = v;
|
||||||
|
}
|
||||||
|
scaleSnappingCM.ButtonClicked += button =>
|
||||||
|
{
|
||||||
|
var v = (float)button.Tag;
|
||||||
|
transformGizmo.ScaleSnapValue = v;
|
||||||
|
scaleSnapping.Text = v.ToString();
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("ScaleSnapValue", transformGizmo.ScaleSnapValue.ToString("N"));
|
||||||
|
};
|
||||||
|
scaleSnappingCM.VisibleChanged += control =>
|
||||||
|
{
|
||||||
|
if (control.Visible == false)
|
||||||
|
return;
|
||||||
|
var ccm = (ContextMenu)control;
|
||||||
|
foreach (var e in ccm.Items)
|
||||||
|
{
|
||||||
|
if (e is ContextMenuButton b)
|
||||||
|
{
|
||||||
|
var v = (float)b.Tag;
|
||||||
|
b.Icon = Mathf.Abs(transformGizmo.ScaleSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
scaleSnapping.Parent = scaleSnappingWidget;
|
||||||
|
scaleSnappingWidget.Parent = viewport;
|
||||||
|
|
||||||
|
// Rotation snapping widget
|
||||||
|
var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
var enableRotateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.RotateSnap32, null, true)
|
||||||
|
{
|
||||||
|
Checked = transformGizmo.RotationSnapEnabled,
|
||||||
|
TooltipText = "Enable rotation snapping",
|
||||||
|
Parent = rotateSnappingWidget
|
||||||
|
};
|
||||||
|
enableRotateSnapping.Toggled += _ =>
|
||||||
|
{
|
||||||
|
transformGizmo.RotationSnapEnabled = !transformGizmo.RotationSnapEnabled;
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("RotationSnapState", transformGizmo.RotationSnapEnabled.ToString());
|
||||||
|
};
|
||||||
|
var rotateSnappingCM = new ContextMenu();
|
||||||
|
var rotateSnapping = new ViewportWidgetButton(transformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM)
|
||||||
|
{
|
||||||
|
TooltipText = "Rotation snapping values"
|
||||||
|
};
|
||||||
|
for (int i = 0; i < RotateSnapValues.Length; i++)
|
||||||
|
{
|
||||||
|
var v = RotateSnapValues[i];
|
||||||
|
var button = rotateSnappingCM.AddButton(v.ToString());
|
||||||
|
button.Tag = v;
|
||||||
|
}
|
||||||
|
rotateSnappingCM.ButtonClicked += button =>
|
||||||
|
{
|
||||||
|
var v = (float)button.Tag;
|
||||||
|
transformGizmo.RotationSnapValue = v;
|
||||||
|
rotateSnapping.Text = v.ToString();
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("RotationSnapValue", transformGizmo.RotationSnapValue.ToString("N"));
|
||||||
|
};
|
||||||
|
rotateSnappingCM.VisibleChanged += control =>
|
||||||
|
{
|
||||||
|
if (control.Visible == false)
|
||||||
|
return;
|
||||||
|
var ccm = (ContextMenu)control;
|
||||||
|
foreach (var e in ccm.Items)
|
||||||
|
{
|
||||||
|
if (e is ContextMenuButton b)
|
||||||
|
{
|
||||||
|
var v = (float)b.Tag;
|
||||||
|
b.Icon = Mathf.Abs(transformGizmo.RotationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rotateSnapping.Parent = rotateSnappingWidget;
|
||||||
|
rotateSnappingWidget.Parent = viewport;
|
||||||
|
|
||||||
|
// Translation snapping widget
|
||||||
|
var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.Grid32, null, true)
|
||||||
|
{
|
||||||
|
Checked = transformGizmo.TranslationSnapEnable,
|
||||||
|
TooltipText = "Enable position snapping",
|
||||||
|
Parent = translateSnappingWidget
|
||||||
|
};
|
||||||
|
enableTranslateSnapping.Toggled += _ =>
|
||||||
|
{
|
||||||
|
transformGizmo.TranslationSnapEnable = !transformGizmo.TranslationSnapEnable;
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("TranslateSnapState", transformGizmo.TranslationSnapEnable.ToString());
|
||||||
|
};
|
||||||
|
var translateSnappingCM = new ContextMenu();
|
||||||
|
var translateSnapping = new ViewportWidgetButton(transformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM)
|
||||||
|
{
|
||||||
|
TooltipText = "Position snapping values"
|
||||||
|
};
|
||||||
|
if (transformGizmo.TranslationSnapValue < 0.0f)
|
||||||
|
translateSnapping.Text = "Bounding Box";
|
||||||
|
for (int i = 0; i < TranslateSnapValues.Length; i++)
|
||||||
|
{
|
||||||
|
var v = TranslateSnapValues[i];
|
||||||
|
var button = translateSnappingCM.AddButton(v.ToString());
|
||||||
|
button.Tag = v;
|
||||||
|
}
|
||||||
|
var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume");
|
||||||
|
buttonBB.Tag = -1.0f;
|
||||||
|
translateSnappingCM.ButtonClicked += button =>
|
||||||
|
{
|
||||||
|
var v = (float)button.Tag;
|
||||||
|
transformGizmo.TranslationSnapValue = v;
|
||||||
|
if (v < 0.0f)
|
||||||
|
translateSnapping.Text = "Bounding Box";
|
||||||
|
else
|
||||||
|
translateSnapping.Text = v.ToString();
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("TranslateSnapValue", transformGizmo.TranslationSnapValue.ToString("N"));
|
||||||
|
};
|
||||||
|
translateSnappingCM.VisibleChanged += control =>
|
||||||
|
{
|
||||||
|
if (control.Visible == false)
|
||||||
|
return;
|
||||||
|
var ccm = (ContextMenu)control;
|
||||||
|
foreach (var e in ccm.Items)
|
||||||
|
{
|
||||||
|
if (e is ContextMenuButton b)
|
||||||
|
{
|
||||||
|
var v = (float)b.Tag;
|
||||||
|
b.Icon = Mathf.Abs(transformGizmo.TranslationSnapValue - v) < 0.001f ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
translateSnapping.Parent = translateSnappingWidget;
|
||||||
|
translateSnappingWidget.Parent = viewport;
|
||||||
|
|
||||||
|
// Gizmo mode widget
|
||||||
|
var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
var gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true)
|
||||||
|
{
|
||||||
|
Tag = TransformGizmoBase.Mode.Translate,
|
||||||
|
TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})",
|
||||||
|
Checked = true,
|
||||||
|
Parent = gizmoMode
|
||||||
|
};
|
||||||
|
gizmoModeTranslate.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate;
|
||||||
|
var gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true)
|
||||||
|
{
|
||||||
|
Tag = TransformGizmoBase.Mode.Rotate,
|
||||||
|
TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})",
|
||||||
|
Parent = gizmoMode
|
||||||
|
};
|
||||||
|
gizmoModeRotate.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate;
|
||||||
|
var gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true)
|
||||||
|
{
|
||||||
|
Tag = TransformGizmoBase.Mode.Scale,
|
||||||
|
TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})",
|
||||||
|
Parent = gizmoMode
|
||||||
|
};
|
||||||
|
gizmoModeScale.Toggled += _ => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale;
|
||||||
|
gizmoMode.Parent = viewport;
|
||||||
|
transformGizmo.ModeChanged += () =>
|
||||||
|
{
|
||||||
|
var mode = transformGizmo.ActiveMode;
|
||||||
|
gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate;
|
||||||
|
gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate;
|
||||||
|
gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setup input actions
|
||||||
|
viewport.InputActions.Add(options => options.TranslateMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
|
||||||
|
viewport.InputActions.Add(options => options.RotateMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
|
||||||
|
viewport.InputActions.Add(options => options.ScaleMode, () => transformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
|
||||||
|
viewport.InputActions.Add(options => options.ToggleTransformSpace, () =>
|
||||||
|
{
|
||||||
|
transformGizmo.ToggleTransformSpace();
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString());
|
||||||
|
transformSpaceToggle.Checked = !transformSpaceToggle.Checked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static readonly float[] TranslateSnapValues =
|
||||||
|
{
|
||||||
|
0.1f,
|
||||||
|
0.5f,
|
||||||
|
1.0f,
|
||||||
|
5.0f,
|
||||||
|
10.0f,
|
||||||
|
100.0f,
|
||||||
|
1000.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
internal static readonly float[] RotateSnapValues =
|
||||||
|
{
|
||||||
|
1.0f,
|
||||||
|
5.0f,
|
||||||
|
10.0f,
|
||||||
|
15.0f,
|
||||||
|
30.0f,
|
||||||
|
45.0f,
|
||||||
|
60.0f,
|
||||||
|
90.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
internal static readonly float[] ScaleSnapValues =
|
||||||
|
{
|
||||||
|
0.05f,
|
||||||
|
0.1f,
|
||||||
|
0.25f,
|
||||||
|
0.5f,
|
||||||
|
1.0f,
|
||||||
|
2.0f,
|
||||||
|
4.0f,
|
||||||
|
6.0f,
|
||||||
|
8.0f,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using FlaxEditor.Viewport.Cameras;
|
|||||||
using FlaxEditor.Viewport.Widgets;
|
using FlaxEditor.Viewport.Widgets;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using JsonSerializer = FlaxEngine.Json.JsonSerializer;
|
using JsonSerializer = FlaxEngine.Json.JsonSerializer;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport
|
namespace FlaxEditor.Viewport
|
||||||
@@ -154,6 +153,7 @@ namespace FlaxEditor.Viewport
|
|||||||
|
|
||||||
// Input
|
// Input
|
||||||
|
|
||||||
|
internal bool _disableInputUpdate;
|
||||||
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
||||||
private int _deltaFilteringStep;
|
private int _deltaFilteringStep;
|
||||||
private Float2 _startPos;
|
private Float2 _startPos;
|
||||||
@@ -704,9 +704,9 @@ namespace FlaxEditor.Viewport
|
|||||||
// Camera Viewpoints
|
// Camera Viewpoints
|
||||||
{
|
{
|
||||||
var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu;
|
var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu;
|
||||||
for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++)
|
for (int i = 0; i < CameraViewpointValues.Length; i++)
|
||||||
{
|
{
|
||||||
var co = EditorViewportCameraViewpointValues[i];
|
var co = CameraViewpointValues[i];
|
||||||
var button = cameraView.AddButton(co.Name);
|
var button = cameraView.AddButton(co.Name);
|
||||||
button.Tag = co.Orientation;
|
button.Tag = co.Orientation;
|
||||||
}
|
}
|
||||||
@@ -899,9 +899,9 @@ namespace FlaxEditor.Viewport
|
|||||||
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
|
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
|
||||||
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32;
|
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32;
|
||||||
viewFlags.AddSeparator();
|
viewFlags.AddSeparator();
|
||||||
for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++)
|
for (int i = 0; i < ViewFlagsValues.Length; i++)
|
||||||
{
|
{
|
||||||
var v = EditorViewportViewFlagsValues[i];
|
var v = ViewFlagsValues[i];
|
||||||
var button = viewFlags.AddButton(v.Name);
|
var button = viewFlags.AddButton(v.Name);
|
||||||
button.CloseMenuOnClick = false;
|
button.CloseMenuOnClick = false;
|
||||||
button.Tag = v.Mode;
|
button.Tag = v.Mode;
|
||||||
@@ -933,9 +933,9 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
debugView.AddSeparator();
|
debugView.AddSeparator();
|
||||||
for (int i = 0; i < EditorViewportViewModeValues.Length; i++)
|
for (int i = 0; i < ViewModeValues.Length; i++)
|
||||||
{
|
{
|
||||||
ref var v = ref EditorViewportViewModeValues[i];
|
ref var v = ref ViewModeValues[i];
|
||||||
if (v.Options != null)
|
if (v.Options != null)
|
||||||
{
|
{
|
||||||
var childMenu = debugView.AddChildMenu(v.Name).ContextMenu;
|
var childMenu = debugView.AddChildMenu(v.Name).ContextMenu;
|
||||||
@@ -989,12 +989,12 @@ namespace FlaxEditor.Viewport
|
|||||||
#endregion View mode widget
|
#endregion View mode widget
|
||||||
}
|
}
|
||||||
|
|
||||||
InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Top").Orientation)));
|
InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Top").Orientation)));
|
||||||
InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation)));
|
InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation)));
|
||||||
InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Front").Orientation)));
|
InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Front").Orientation)));
|
||||||
InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Back").Orientation)));
|
InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Back").Orientation)));
|
||||||
InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Right").Orientation)));
|
InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Right").Orientation)));
|
||||||
InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Left").Orientation)));
|
InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Left").Orientation)));
|
||||||
InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown);
|
InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown);
|
||||||
InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1));
|
InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1));
|
||||||
InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1));
|
InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1));
|
||||||
@@ -1497,6 +1497,9 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
base.Update(deltaTime);
|
base.Update(deltaTime);
|
||||||
|
|
||||||
|
if (_disableInputUpdate)
|
||||||
|
return;
|
||||||
|
|
||||||
// Update camera
|
// Update camera
|
||||||
bool useMovementSpeed = false;
|
bool useMovementSpeed = false;
|
||||||
if (_camera != null)
|
if (_camera != null)
|
||||||
@@ -1535,7 +1538,7 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
|
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
|
||||||
_prevInput = _input;
|
_prevInput = _input;
|
||||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl));
|
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||||
if (canUseInput && ContainsFocus && hit == null)
|
if (canUseInput && ContainsFocus && hit == null)
|
||||||
_input.Gather(win.Window, useMouse);
|
_input.Gather(win.Window, useMouse);
|
||||||
else
|
else
|
||||||
@@ -1868,7 +1871,7 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly CameraViewpoint[] EditorViewportCameraViewpointValues =
|
private readonly CameraViewpoint[] CameraViewpointValues =
|
||||||
{
|
{
|
||||||
new CameraViewpoint("Front", new Float3(0, 180, 0)),
|
new CameraViewpoint("Front", new Float3(0, 180, 0)),
|
||||||
new CameraViewpoint("Back", new Float3(0, 0, 0)),
|
new CameraViewpoint("Back", new Float3(0, 0, 0)),
|
||||||
@@ -1899,7 +1902,7 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly ViewModeOptions[] EditorViewportViewModeValues =
|
private static readonly ViewModeOptions[] ViewModeValues =
|
||||||
{
|
{
|
||||||
new ViewModeOptions(ViewMode.Default, "Default"),
|
new ViewModeOptions(ViewMode.Default, "Default"),
|
||||||
new ViewModeOptions(ViewMode.Unlit, "Unlit"),
|
new ViewModeOptions(ViewMode.Unlit, "Unlit"),
|
||||||
@@ -1971,7 +1974,7 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly ViewFlagOptions[] EditorViewportViewFlagsValues =
|
private static readonly ViewFlagOptions[] ViewFlagsValues =
|
||||||
{
|
{
|
||||||
new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing"),
|
new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing"),
|
||||||
new ViewFlagOptions(ViewFlags.Shadows, "Shadows"),
|
new ViewFlagOptions(ViewFlags.Shadows, "Shadows"),
|
||||||
@@ -2006,16 +2009,13 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
if (cm.Visible == false)
|
if (cm.Visible == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ccm = (ContextMenu)cm;
|
var ccm = (ContextMenu)cm;
|
||||||
foreach (var e in ccm.Items)
|
foreach (var e in ccm.Items)
|
||||||
{
|
{
|
||||||
if (e is ContextMenuButton b && b.Tag != null)
|
if (e is ContextMenuButton b && b.Tag != null)
|
||||||
{
|
{
|
||||||
var v = (ViewFlags)b.Tag;
|
var v = (ViewFlags)b.Tag;
|
||||||
b.Icon = (Task.View.Flags & v) != 0
|
b.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
? Style.Current.CheckBoxTick
|
|
||||||
: SpriteHandle.Invalid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2024,7 +2024,6 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
if (cm.Visible == false)
|
if (cm.Visible == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ccm = (ContextMenu)cm;
|
var ccm = (ContextMenu)cm;
|
||||||
var layersMask = Task.ViewLayersMask;
|
var layersMask = Task.ViewLayersMask;
|
||||||
foreach (var e in ccm.Items)
|
foreach (var e in ccm.Items)
|
||||||
|
|||||||
@@ -2,15 +2,12 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.Gizmo;
|
using FlaxEditor.Gizmo;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEditor.Viewport.Cameras;
|
|
||||||
using FlaxEditor.Viewport.Modes;
|
using FlaxEditor.Viewport.Modes;
|
||||||
using FlaxEditor.Viewport.Widgets;
|
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
@@ -28,13 +25,6 @@ namespace FlaxEditor.Viewport
|
|||||||
|
|
||||||
private readonly ContextMenuButton _showGridButton;
|
private readonly ContextMenuButton _showGridButton;
|
||||||
private readonly ContextMenuButton _showNavigationButton;
|
private readonly ContextMenuButton _showNavigationButton;
|
||||||
private readonly ViewportWidgetButton _gizmoModeTranslate;
|
|
||||||
private readonly ViewportWidgetButton _gizmoModeRotate;
|
|
||||||
private readonly ViewportWidgetButton _gizmoModeScale;
|
|
||||||
|
|
||||||
private readonly ViewportWidgetButton _translateSnapping;
|
|
||||||
private readonly ViewportWidgetButton _rotateSnapping;
|
|
||||||
private readonly ViewportWidgetButton _scaleSnapping;
|
|
||||||
|
|
||||||
private SelectionOutline _customSelectionOutline;
|
private SelectionOutline _customSelectionOutline;
|
||||||
|
|
||||||
@@ -196,7 +186,6 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
_editor = editor;
|
_editor = editor;
|
||||||
DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType, ValidateDragScriptItem);
|
DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType, ValidateDragScriptItem);
|
||||||
var inputOptions = editor.Options.Options.Input;
|
|
||||||
|
|
||||||
// Prepare rendering task
|
// Prepare rendering task
|
||||||
Task.ActorsSource = ActorsSources.Scenes;
|
Task.ActorsSource = ActorsSources.Scenes;
|
||||||
@@ -222,8 +211,7 @@ namespace FlaxEditor.Viewport
|
|||||||
// Add transformation gizmo
|
// Add transformation gizmo
|
||||||
TransformGizmo = new TransformGizmo(this);
|
TransformGizmo = new TransformGizmo(this);
|
||||||
TransformGizmo.ApplyTransformation += ApplyTransform;
|
TransformGizmo.ApplyTransformation += ApplyTransform;
|
||||||
TransformGizmo.ModeChanged += OnGizmoModeChanged;
|
TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate;
|
||||||
TransformGizmo.Duplicate += Editor.Instance.SceneEditing.Duplicate;
|
|
||||||
Gizmos.Active = TransformGizmo;
|
Gizmos.Active = TransformGizmo;
|
||||||
|
|
||||||
// Add grid
|
// Add grid
|
||||||
@@ -232,144 +220,8 @@ namespace FlaxEditor.Viewport
|
|||||||
|
|
||||||
editor.SceneEditing.SelectionChanged += OnSelectionChanged;
|
editor.SceneEditing.SelectionChanged += OnSelectionChanged;
|
||||||
|
|
||||||
// Initialize snapping enabled from cached values
|
// Gizmo widgets
|
||||||
if (_editor.ProjectCache.TryGetCustomData("TranslateSnapState", out var cachedState))
|
AddGizmoViewportWidgets(this, TransformGizmo);
|
||||||
TransformGizmo.TranslationSnapEnable = bool.Parse(cachedState);
|
|
||||||
if (_editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedState))
|
|
||||||
TransformGizmo.RotationSnapEnabled = bool.Parse(cachedState);
|
|
||||||
if (_editor.ProjectCache.TryGetCustomData("ScaleSnapState", out cachedState))
|
|
||||||
TransformGizmo.ScaleSnapEnabled = bool.Parse(cachedState);
|
|
||||||
if (_editor.ProjectCache.TryGetCustomData("TranslateSnapValue", out cachedState))
|
|
||||||
TransformGizmo.TranslationSnapValue = float.Parse(cachedState);
|
|
||||||
if (_editor.ProjectCache.TryGetCustomData("RotationSnapValue", out cachedState))
|
|
||||||
TransformGizmo.RotationSnapValue = float.Parse(cachedState);
|
|
||||||
if (_editor.ProjectCache.TryGetCustomData("ScaleSnapValue", out cachedState))
|
|
||||||
TransformGizmo.ScaleSnapValue = float.Parse(cachedState);
|
|
||||||
if (_editor.ProjectCache.TryGetCustomData("TransformSpaceState", out cachedState) && Enum.TryParse(cachedState, out TransformGizmoBase.TransformSpace space))
|
|
||||||
TransformGizmo.ActiveTransformSpace = space;
|
|
||||||
|
|
||||||
// Transform space widget
|
|
||||||
var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, editor.Icons.Globe32, null, true)
|
|
||||||
{
|
|
||||||
Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World,
|
|
||||||
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
|
|
||||||
Parent = transformSpaceWidget
|
|
||||||
};
|
|
||||||
transformSpaceToggle.Toggled += OnTransformSpaceToggle;
|
|
||||||
transformSpaceWidget.Parent = this;
|
|
||||||
|
|
||||||
// Scale snapping widget
|
|
||||||
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
var enableScaleSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.ScaleSnap32, null, true)
|
|
||||||
{
|
|
||||||
Checked = TransformGizmo.ScaleSnapEnabled,
|
|
||||||
TooltipText = "Enable scale snapping",
|
|
||||||
Parent = scaleSnappingWidget
|
|
||||||
};
|
|
||||||
enableScaleSnapping.Toggled += OnScaleSnappingToggle;
|
|
||||||
|
|
||||||
var scaleSnappingCM = new ContextMenu();
|
|
||||||
_scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM)
|
|
||||||
{
|
|
||||||
TooltipText = "Scale snapping values"
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < EditorViewportScaleSnapValues.Length; i++)
|
|
||||||
{
|
|
||||||
var v = EditorViewportScaleSnapValues[i];
|
|
||||||
var button = scaleSnappingCM.AddButton(v.ToString());
|
|
||||||
button.Tag = v;
|
|
||||||
}
|
|
||||||
scaleSnappingCM.ButtonClicked += OnWidgetScaleSnapClick;
|
|
||||||
scaleSnappingCM.VisibleChanged += OnWidgetScaleSnapShowHide;
|
|
||||||
_scaleSnapping.Parent = scaleSnappingWidget;
|
|
||||||
scaleSnappingWidget.Parent = this;
|
|
||||||
|
|
||||||
// Rotation snapping widget
|
|
||||||
var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
var enableRotateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.RotateSnap32, null, true)
|
|
||||||
{
|
|
||||||
Checked = TransformGizmo.RotationSnapEnabled,
|
|
||||||
TooltipText = "Enable rotation snapping",
|
|
||||||
Parent = rotateSnappingWidget
|
|
||||||
};
|
|
||||||
enableRotateSnapping.Toggled += OnRotateSnappingToggle;
|
|
||||||
|
|
||||||
var rotateSnappingCM = new ContextMenu();
|
|
||||||
_rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM)
|
|
||||||
{
|
|
||||||
TooltipText = "Rotation snapping values"
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < EditorViewportRotateSnapValues.Length; i++)
|
|
||||||
{
|
|
||||||
var v = EditorViewportRotateSnapValues[i];
|
|
||||||
var button = rotateSnappingCM.AddButton(v.ToString());
|
|
||||||
button.Tag = v;
|
|
||||||
}
|
|
||||||
rotateSnappingCM.ButtonClicked += OnWidgetRotateSnapClick;
|
|
||||||
rotateSnappingCM.VisibleChanged += OnWidgetRotateSnapShowHide;
|
|
||||||
_rotateSnapping.Parent = rotateSnappingWidget;
|
|
||||||
rotateSnappingWidget.Parent = this;
|
|
||||||
|
|
||||||
// Translation snapping widget
|
|
||||||
var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, editor.Icons.Grid32, null, true)
|
|
||||||
{
|
|
||||||
Checked = TransformGizmo.TranslationSnapEnable,
|
|
||||||
TooltipText = "Enable position snapping",
|
|
||||||
Parent = translateSnappingWidget
|
|
||||||
};
|
|
||||||
enableTranslateSnapping.Toggled += OnTranslateSnappingToggle;
|
|
||||||
|
|
||||||
var translateSnappingCM = new ContextMenu();
|
|
||||||
_translateSnapping = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM)
|
|
||||||
{
|
|
||||||
TooltipText = "Position snapping values"
|
|
||||||
};
|
|
||||||
if (TransformGizmo.TranslationSnapValue < 0.0f)
|
|
||||||
_translateSnapping.Text = "Bounding Box";
|
|
||||||
|
|
||||||
for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++)
|
|
||||||
{
|
|
||||||
var v = EditorViewportTranslateSnapValues[i];
|
|
||||||
var button = translateSnappingCM.AddButton(v.ToString());
|
|
||||||
button.Tag = v;
|
|
||||||
}
|
|
||||||
var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume");
|
|
||||||
buttonBB.Tag = -1.0f;
|
|
||||||
|
|
||||||
translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick;
|
|
||||||
translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide;
|
|
||||||
_translateSnapping.Parent = translateSnappingWidget;
|
|
||||||
translateSnappingWidget.Parent = this;
|
|
||||||
|
|
||||||
// Gizmo mode widget
|
|
||||||
var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
_gizmoModeTranslate = new ViewportWidgetButton(string.Empty, editor.Icons.Translate32, null, true)
|
|
||||||
{
|
|
||||||
Tag = TransformGizmoBase.Mode.Translate,
|
|
||||||
TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})",
|
|
||||||
Checked = true,
|
|
||||||
Parent = gizmoMode
|
|
||||||
};
|
|
||||||
_gizmoModeTranslate.Toggled += OnGizmoModeToggle;
|
|
||||||
_gizmoModeRotate = new ViewportWidgetButton(string.Empty, editor.Icons.Rotate32, null, true)
|
|
||||||
{
|
|
||||||
Tag = TransformGizmoBase.Mode.Rotate,
|
|
||||||
TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})",
|
|
||||||
Parent = gizmoMode
|
|
||||||
};
|
|
||||||
_gizmoModeRotate.Toggled += OnGizmoModeToggle;
|
|
||||||
_gizmoModeScale = new ViewportWidgetButton(string.Empty, editor.Icons.Scale32, null, true)
|
|
||||||
{
|
|
||||||
Tag = TransformGizmoBase.Mode.Scale,
|
|
||||||
TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})",
|
|
||||||
Parent = gizmoMode
|
|
||||||
};
|
|
||||||
_gizmoModeScale.Toggled += OnGizmoModeToggle;
|
|
||||||
gizmoMode.Parent = this;
|
|
||||||
|
|
||||||
// Show grid widget
|
// Show grid widget
|
||||||
_showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled);
|
_showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled);
|
||||||
@@ -400,14 +252,6 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup input actions
|
// Setup input actions
|
||||||
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
|
|
||||||
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
|
|
||||||
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
|
|
||||||
InputActions.Add(options => options.ToggleTransformSpace, () =>
|
|
||||||
{
|
|
||||||
OnTransformSpaceToggle(transformSpaceToggle);
|
|
||||||
transformSpaceToggle.Checked = !transformSpaceToggle.Checked;
|
|
||||||
});
|
|
||||||
InputActions.Add(options => options.LockFocusSelection, LockFocusSelection);
|
InputActions.Add(options => options.LockFocusSelection, LockFocusSelection);
|
||||||
InputActions.Add(options => options.FocusSelection, FocusSelection);
|
InputActions.Add(options => options.FocusSelection, FocusSelection);
|
||||||
InputActions.Add(options => options.RotateSelection, RotateSelection);
|
InputActions.Add(options => options.RotateSelection, RotateSelection);
|
||||||
@@ -486,7 +330,7 @@ namespace FlaxEditor.Viewport
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Spawn
|
// Spawn
|
||||||
Editor.Instance.SceneEditing.Spawn(actor, parent);
|
_editor.SceneEditing.Spawn(actor, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBegin(RenderTask task, GPUContext context)
|
private void OnBegin(RenderTask task, GPUContext context)
|
||||||
@@ -552,7 +396,7 @@ namespace FlaxEditor.Viewport
|
|||||||
var task = renderContext.Task;
|
var task = renderContext.Task;
|
||||||
|
|
||||||
// Render editor primitives, gizmo and debug shapes in debug view modes
|
// Render editor primitives, gizmo and debug shapes in debug view modes
|
||||||
// Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers
|
// Note: can use Output buffer as both input and output because EditorPrimitives is using an intermediate buffer
|
||||||
if (EditorPrimitives && EditorPrimitives.CanRender())
|
if (EditorPrimitives && EditorPrimitives.CanRender())
|
||||||
{
|
{
|
||||||
EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output);
|
EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output);
|
||||||
@@ -581,161 +425,6 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGizmoModeToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.ActiveMode = (TransformGizmoBase.Mode)(int)button.Tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTranslateSnappingToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable;
|
|
||||||
_editor.ProjectCache.SetCustomData("TranslateSnapState", TransformGizmo.TranslationSnapEnable.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRotateSnappingToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled;
|
|
||||||
_editor.ProjectCache.SetCustomData("RotationSnapState", TransformGizmo.RotationSnapEnabled.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnScaleSnappingToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled;
|
|
||||||
_editor.ProjectCache.SetCustomData("ScaleSnapState", TransformGizmo.ScaleSnapEnabled.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTransformSpaceToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.ToggleTransformSpace();
|
|
||||||
_editor.ProjectCache.SetCustomData("TransformSpaceState", TransformGizmo.ActiveTransformSpace.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGizmoModeChanged()
|
|
||||||
{
|
|
||||||
// Update all viewport widgets status
|
|
||||||
var mode = TransformGizmo.ActiveMode;
|
|
||||||
_gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate;
|
|
||||||
_gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate;
|
|
||||||
_gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly float[] EditorViewportScaleSnapValues =
|
|
||||||
{
|
|
||||||
0.05f,
|
|
||||||
0.1f,
|
|
||||||
0.25f,
|
|
||||||
0.5f,
|
|
||||||
1.0f,
|
|
||||||
2.0f,
|
|
||||||
4.0f,
|
|
||||||
6.0f,
|
|
||||||
8.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
private void OnWidgetScaleSnapClick(ContextMenuButton button)
|
|
||||||
{
|
|
||||||
var v = (float)button.Tag;
|
|
||||||
TransformGizmo.ScaleSnapValue = v;
|
|
||||||
_scaleSnapping.Text = v.ToString();
|
|
||||||
_editor.ProjectCache.SetCustomData("ScaleSnapValue", TransformGizmo.ScaleSnapValue.ToString("N"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWidgetScaleSnapShowHide(Control control)
|
|
||||||
{
|
|
||||||
if (control.Visible == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ccm = (ContextMenu)control;
|
|
||||||
foreach (var e in ccm.Items)
|
|
||||||
{
|
|
||||||
if (e is ContextMenuButton b)
|
|
||||||
{
|
|
||||||
var v = (float)b.Tag;
|
|
||||||
b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f
|
|
||||||
? Style.Current.CheckBoxTick
|
|
||||||
: SpriteHandle.Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly float[] EditorViewportRotateSnapValues =
|
|
||||||
{
|
|
||||||
1.0f,
|
|
||||||
5.0f,
|
|
||||||
10.0f,
|
|
||||||
15.0f,
|
|
||||||
30.0f,
|
|
||||||
45.0f,
|
|
||||||
60.0f,
|
|
||||||
90.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
private void OnWidgetRotateSnapClick(ContextMenuButton button)
|
|
||||||
{
|
|
||||||
var v = (float)button.Tag;
|
|
||||||
TransformGizmo.RotationSnapValue = v;
|
|
||||||
_rotateSnapping.Text = v.ToString();
|
|
||||||
_editor.ProjectCache.SetCustomData("RotationSnapValue", TransformGizmo.RotationSnapValue.ToString("N"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWidgetRotateSnapShowHide(Control control)
|
|
||||||
{
|
|
||||||
if (control.Visible == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ccm = (ContextMenu)control;
|
|
||||||
foreach (var e in ccm.Items)
|
|
||||||
{
|
|
||||||
if (e is ContextMenuButton b)
|
|
||||||
{
|
|
||||||
var v = (float)b.Tag;
|
|
||||||
b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f
|
|
||||||
? Style.Current.CheckBoxTick
|
|
||||||
: SpriteHandle.Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly float[] EditorViewportTranslateSnapValues =
|
|
||||||
{
|
|
||||||
0.1f,
|
|
||||||
0.5f,
|
|
||||||
1.0f,
|
|
||||||
5.0f,
|
|
||||||
10.0f,
|
|
||||||
100.0f,
|
|
||||||
1000.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
private void OnWidgetTranslateSnapClick(ContextMenuButton button)
|
|
||||||
{
|
|
||||||
var v = (float)button.Tag;
|
|
||||||
TransformGizmo.TranslationSnapValue = v;
|
|
||||||
if (v < 0.0f)
|
|
||||||
_translateSnapping.Text = "Bounding Box";
|
|
||||||
else
|
|
||||||
_translateSnapping.Text = v.ToString();
|
|
||||||
_editor.ProjectCache.SetCustomData("TranslateSnapValue", TransformGizmo.TranslationSnapValue.ToString("N"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWidgetTranslateSnapShowHide(Control control)
|
|
||||||
{
|
|
||||||
if (control.Visible == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ccm = (ContextMenu)control;
|
|
||||||
foreach (var e in ccm.Items)
|
|
||||||
{
|
|
||||||
if (e is ContextMenuButton b)
|
|
||||||
{
|
|
||||||
var v = (float)b.Tag;
|
|
||||||
b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f
|
|
||||||
? Style.Current.CheckBoxTick
|
|
||||||
: SpriteHandle.Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSelectionChanged()
|
private void OnSelectionChanged()
|
||||||
{
|
{
|
||||||
var selection = _editor.SceneEditing.Selection;
|
var selection = _editor.SceneEditing.Selection;
|
||||||
@@ -761,7 +450,7 @@ namespace FlaxEditor.Viewport
|
|||||||
Vector3 gizmoPosition = TransformGizmo.Position;
|
Vector3 gizmoPosition = TransformGizmo.Position;
|
||||||
|
|
||||||
// Rotate selected objects
|
// Rotate selected objects
|
||||||
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
|
bool isPlayMode = _editor.StateMachine.IsPlayMode;
|
||||||
TransformGizmo.StartTransforming();
|
TransformGizmo.StartTransforming();
|
||||||
for (int i = 0; i < selection.Count; i++)
|
for (int i = 0; i < selection.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -819,14 +508,7 @@ namespace FlaxEditor.Viewport
|
|||||||
/// <param name="orientation">The target view orientation.</param>
|
/// <param name="orientation">The target view orientation.</param>
|
||||||
public void FocusSelection(ref Quaternion orientation)
|
public void FocusSelection(ref Quaternion orientation)
|
||||||
{
|
{
|
||||||
if (TransformGizmo.SelectedParents.Count == 0)
|
ViewportCamera.FocusSelection(Gizmos, ref orientation);
|
||||||
return;
|
|
||||||
|
|
||||||
var gizmoBounds = Gizmos.Active.FocusBounds;
|
|
||||||
if (gizmoBounds != BoundingSphere.Empty)
|
|
||||||
((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation);
|
|
||||||
else
|
|
||||||
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -843,7 +525,7 @@ namespace FlaxEditor.Viewport
|
|||||||
Vector3 gizmoPosition = TransformGizmo.Position;
|
Vector3 gizmoPosition = TransformGizmo.Position;
|
||||||
|
|
||||||
// Transform selected objects
|
// Transform selected objects
|
||||||
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
|
bool isPlayMode = _editor.StateMachine.IsPlayMode;
|
||||||
for (int i = 0; i < selection.Count; i++)
|
for (int i = 0; i < selection.Count; i++)
|
||||||
{
|
{
|
||||||
var obj = selection[i];
|
var obj = selection[i];
|
||||||
@@ -985,7 +667,14 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
var parent = actor.Parent ?? Level.GetScene(0);
|
var parent = actor.Parent ?? Level.GetScene(0);
|
||||||
actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null);
|
actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null);
|
||||||
Editor.Instance.SceneEditing.Spawn(actor);
|
_editor.SceneEditing.Spawn(actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OpenContextMenu()
|
||||||
|
{
|
||||||
|
var mouse = PointFromWindow(Root.MousePosition);
|
||||||
|
_editor.Windows.SceneWin.ShowContextMenu(this, mouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.Gizmo;
|
using FlaxEditor.Gizmo;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEditor.Viewport.Cameras;
|
using FlaxEditor.Viewport.Cameras;
|
||||||
using FlaxEditor.Viewport.Previews;
|
using FlaxEditor.Viewport.Previews;
|
||||||
using FlaxEditor.Viewport.Widgets;
|
|
||||||
using FlaxEditor.Windows.Assets;
|
using FlaxEditor.Windows.Assets;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
@@ -30,7 +28,6 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
public PrefabWindowViewport Viewport;
|
public PrefabWindowViewport Viewport;
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override bool CanRender()
|
public override bool CanRender()
|
||||||
{
|
{
|
||||||
return (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled;
|
return (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled;
|
||||||
@@ -42,19 +39,34 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HideInEditor]
|
||||||
|
private sealed class PrefabUIEditorRoot : UIEditorRoot
|
||||||
|
{
|
||||||
|
private readonly PrefabWindowViewport _viewport;
|
||||||
|
private bool UI => _viewport._hasUILinkedCached;
|
||||||
|
|
||||||
|
public PrefabUIEditorRoot(PrefabWindowViewport viewport)
|
||||||
|
: base(true)
|
||||||
|
{
|
||||||
|
_viewport = viewport;
|
||||||
|
Parent = viewport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool EnableInputs => !UI;
|
||||||
|
public override bool EnableSelecting => UI;
|
||||||
|
public override bool EnableBackground => UI;
|
||||||
|
public override TransformGizmo TransformGizmo => _viewport.TransformGizmo;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly PrefabWindow _window;
|
private readonly PrefabWindow _window;
|
||||||
private UpdateDelegate _update;
|
private UpdateDelegate _update;
|
||||||
|
|
||||||
private readonly ViewportWidgetButton _gizmoModeTranslate;
|
|
||||||
private readonly ViewportWidgetButton _gizmoModeRotate;
|
|
||||||
private readonly ViewportWidgetButton _gizmoModeScale;
|
|
||||||
|
|
||||||
private ViewportWidgetButton _translateSnappng;
|
|
||||||
private ViewportWidgetButton _rotateSnapping;
|
|
||||||
private ViewportWidgetButton _scaleSnapping;
|
|
||||||
|
|
||||||
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
||||||
private PrefabSpritesRenderer _spritesRenderer;
|
private PrefabSpritesRenderer _spritesRenderer;
|
||||||
|
private IntPtr _tempDebugDrawContext;
|
||||||
|
|
||||||
|
private bool _hasUILinkedCached;
|
||||||
|
private PrefabUIEditorRoot _uiRoot;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drag and drop handlers
|
/// Drag and drop handlers
|
||||||
@@ -107,144 +119,74 @@ namespace FlaxEditor.Viewport
|
|||||||
// Add transformation gizmo
|
// Add transformation gizmo
|
||||||
TransformGizmo = new TransformGizmo(this);
|
TransformGizmo = new TransformGizmo(this);
|
||||||
TransformGizmo.ApplyTransformation += ApplyTransform;
|
TransformGizmo.ApplyTransformation += ApplyTransform;
|
||||||
TransformGizmo.ModeChanged += OnGizmoModeChanged;
|
|
||||||
TransformGizmo.Duplicate += _window.Duplicate;
|
TransformGizmo.Duplicate += _window.Duplicate;
|
||||||
Gizmos.Active = TransformGizmo;
|
Gizmos.Active = TransformGizmo;
|
||||||
|
|
||||||
// Transform space widget
|
// Use custom root for UI controls
|
||||||
var transformSpaceWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
_uiRoot = new PrefabUIEditorRoot(this);
|
||||||
var transformSpaceToggle = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Globe32, null, true)
|
_uiRoot.IndexInParent = 0; // Move viewport down below other widgets in the viewport
|
||||||
{
|
_uiParentLink = _uiRoot.UIRoot;
|
||||||
Checked = TransformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World,
|
|
||||||
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
|
|
||||||
Parent = transformSpaceWidget
|
|
||||||
};
|
|
||||||
transformSpaceToggle.Toggled += OnTransformSpaceToggle;
|
|
||||||
transformSpaceWidget.Parent = this;
|
|
||||||
|
|
||||||
// Scale snapping widget
|
EditorGizmoViewport.AddGizmoViewportWidgets(this, TransformGizmo);
|
||||||
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
var enableScaleSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.ScaleSnap32, null, true)
|
|
||||||
{
|
|
||||||
Checked = TransformGizmo.ScaleSnapEnabled,
|
|
||||||
TooltipText = "Enable scale snapping",
|
|
||||||
Parent = scaleSnappingWidget
|
|
||||||
};
|
|
||||||
enableScaleSnapping.Toggled += OnScaleSnappingToggle;
|
|
||||||
|
|
||||||
var scaleSnappingCM = new ContextMenu();
|
|
||||||
_scaleSnapping = new ViewportWidgetButton(TransformGizmo.ScaleSnapValue.ToString(), SpriteHandle.Invalid, scaleSnappingCM)
|
|
||||||
{
|
|
||||||
TooltipText = "Scale snapping values"
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < EditorViewportScaleSnapValues.Length; i++)
|
|
||||||
{
|
|
||||||
var v = EditorViewportScaleSnapValues[i];
|
|
||||||
var button = scaleSnappingCM.AddButton(v.ToString());
|
|
||||||
button.Tag = v;
|
|
||||||
}
|
|
||||||
scaleSnappingCM.ButtonClicked += OnWidgetScaleSnapClick;
|
|
||||||
scaleSnappingCM.VisibleChanged += OnWidgetScaleSnapShowHide;
|
|
||||||
_scaleSnapping.Parent = scaleSnappingWidget;
|
|
||||||
scaleSnappingWidget.Parent = this;
|
|
||||||
|
|
||||||
// Rotation snapping widget
|
|
||||||
var rotateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
var enableRotateSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.RotateSnap32, null, true)
|
|
||||||
{
|
|
||||||
Checked = TransformGizmo.RotationSnapEnabled,
|
|
||||||
TooltipText = "Enable rotation snapping",
|
|
||||||
Parent = rotateSnappingWidget
|
|
||||||
};
|
|
||||||
enableRotateSnapping.Toggled += OnRotateSnappingToggle;
|
|
||||||
|
|
||||||
var rotateSnappingCM = new ContextMenu();
|
|
||||||
_rotateSnapping = new ViewportWidgetButton(TransformGizmo.RotationSnapValue.ToString(), SpriteHandle.Invalid, rotateSnappingCM)
|
|
||||||
{
|
|
||||||
TooltipText = "Rotation snapping values"
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < EditorViewportRotateSnapValues.Length; i++)
|
|
||||||
{
|
|
||||||
var v = EditorViewportRotateSnapValues[i];
|
|
||||||
var button = rotateSnappingCM.AddButton(v.ToString());
|
|
||||||
button.Tag = v;
|
|
||||||
}
|
|
||||||
rotateSnappingCM.ButtonClicked += OnWidgetRotateSnapClick;
|
|
||||||
rotateSnappingCM.VisibleChanged += OnWidgetRotateSnapShowHide;
|
|
||||||
_rotateSnapping.Parent = rotateSnappingWidget;
|
|
||||||
rotateSnappingWidget.Parent = this;
|
|
||||||
|
|
||||||
// Translation snapping widget
|
|
||||||
var translateSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
var enableTranslateSnapping = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Grid32, null, true)
|
|
||||||
{
|
|
||||||
Checked = TransformGizmo.TranslationSnapEnable,
|
|
||||||
TooltipText = "Enable position snapping",
|
|
||||||
Parent = translateSnappingWidget
|
|
||||||
};
|
|
||||||
enableTranslateSnapping.Toggled += OnTranslateSnappingToggle;
|
|
||||||
|
|
||||||
var translateSnappingCM = new ContextMenu();
|
|
||||||
_translateSnappng = new ViewportWidgetButton(TransformGizmo.TranslationSnapValue.ToString(), SpriteHandle.Invalid, translateSnappingCM)
|
|
||||||
{
|
|
||||||
TooltipText = "Position snapping values"
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++)
|
|
||||||
{
|
|
||||||
var v = EditorViewportTranslateSnapValues[i];
|
|
||||||
var button = translateSnappingCM.AddButton(v.ToString());
|
|
||||||
button.Tag = v;
|
|
||||||
}
|
|
||||||
translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick;
|
|
||||||
translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide;
|
|
||||||
_translateSnappng.Parent = translateSnappingWidget;
|
|
||||||
translateSnappingWidget.Parent = this;
|
|
||||||
|
|
||||||
// Gizmo mode widget
|
|
||||||
var gizmoMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
|
||||||
_gizmoModeTranslate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Translate32, null, true)
|
|
||||||
{
|
|
||||||
Tag = TransformGizmoBase.Mode.Translate,
|
|
||||||
TooltipText = $"Translate gizmo mode ({inputOptions.TranslateMode})",
|
|
||||||
Checked = true,
|
|
||||||
Parent = gizmoMode
|
|
||||||
};
|
|
||||||
_gizmoModeTranslate.Toggled += OnGizmoModeToggle;
|
|
||||||
_gizmoModeRotate = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Rotate32, null, true)
|
|
||||||
{
|
|
||||||
Tag = TransformGizmoBase.Mode.Rotate,
|
|
||||||
TooltipText = $"Rotate gizmo mode ({inputOptions.RotateMode})",
|
|
||||||
Parent = gizmoMode
|
|
||||||
};
|
|
||||||
_gizmoModeRotate.Toggled += OnGizmoModeToggle;
|
|
||||||
_gizmoModeScale = new ViewportWidgetButton(string.Empty, window.Editor.Icons.Scale32, null, true)
|
|
||||||
{
|
|
||||||
Tag = TransformGizmoBase.Mode.Scale,
|
|
||||||
TooltipText = $"Scale gizmo mode ({inputOptions.ScaleMode})",
|
|
||||||
Parent = gizmoMode
|
|
||||||
};
|
|
||||||
_gizmoModeScale.Toggled += OnGizmoModeToggle;
|
|
||||||
gizmoMode.Parent = this;
|
|
||||||
|
|
||||||
// Setup input actions
|
// Setup input actions
|
||||||
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
|
|
||||||
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
|
|
||||||
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
|
|
||||||
InputActions.Add(options => options.ToggleTransformSpace, () =>
|
|
||||||
{
|
|
||||||
OnTransformSpaceToggle(transformSpaceToggle);
|
|
||||||
transformSpaceToggle.Checked = !transformSpaceToggle.Checked;
|
|
||||||
});
|
|
||||||
InputActions.Add(options => options.FocusSelection, ShowSelectedActors);
|
InputActions.Add(options => options.FocusSelection, ShowSelectedActors);
|
||||||
|
|
||||||
SetUpdate(ref _update, OnUpdate);
|
SetUpdate(ref _update, OnUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the viewport's gizmos, especially to toggle between 3D and UI editing modes.
|
||||||
|
/// </summary>
|
||||||
|
internal void UpdateGizmoMode()
|
||||||
|
{
|
||||||
|
// Skip if gizmo mode was unmodified
|
||||||
|
if (_hasUILinked == _hasUILinkedCached)
|
||||||
|
return;
|
||||||
|
_hasUILinkedCached = _hasUILinked;
|
||||||
|
|
||||||
|
if (_hasUILinked)
|
||||||
|
{
|
||||||
|
// UI widget
|
||||||
|
Gizmos.Active = null;
|
||||||
|
ViewportCamera = new UIEditorCamera { UIEditor = _uiRoot };
|
||||||
|
|
||||||
|
// Hide 3D visuals
|
||||||
|
ShowEditorPrimitives = false;
|
||||||
|
ShowDefaultSceneActors = false;
|
||||||
|
ShowDebugDraw = false;
|
||||||
|
|
||||||
|
// Show whole UI on startup
|
||||||
|
var canvas = (CanvasRootControl)_uiParentLink.Children.FirstOrDefault(x => x is CanvasRootControl);
|
||||||
|
if (canvas != null)
|
||||||
|
ViewportCamera.ShowActor(canvas.Canvas);
|
||||||
|
else if (Instance is UIControl)
|
||||||
|
ViewportCamera.ShowActor(Instance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Generic prefab
|
||||||
|
Gizmos.Active = TransformGizmo;
|
||||||
|
ViewportCamera = new FPSCamera();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update default components usage
|
||||||
|
bool defaultFeatures = !_hasUILinked;
|
||||||
|
_disableInputUpdate = _hasUILinked;
|
||||||
|
_spritesRenderer.Enabled = defaultFeatures;
|
||||||
|
SelectionOutline.Enabled = defaultFeatures;
|
||||||
|
_showDefaultSceneButton.Visible = defaultFeatures;
|
||||||
|
_cameraWidget.Visible = defaultFeatures;
|
||||||
|
_cameraButton.Visible = defaultFeatures;
|
||||||
|
_orthographicModeButton.Visible = defaultFeatures;
|
||||||
|
Task.Enabled = defaultFeatures;
|
||||||
|
UseAutomaticTaskManagement = defaultFeatures;
|
||||||
|
TintColor = defaultFeatures ? Color.White : Color.Transparent;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnUpdate(float deltaTime)
|
private void OnUpdate(float deltaTime)
|
||||||
{
|
{
|
||||||
|
UpdateGizmoMode();
|
||||||
for (int i = 0; i < Gizmos.Count; i++)
|
for (int i = 0; i < Gizmos.Count; i++)
|
||||||
{
|
{
|
||||||
Gizmos[i].Update(deltaTime);
|
Gizmos[i].Update(deltaTime);
|
||||||
@@ -259,11 +201,19 @@ namespace FlaxEditor.Viewport
|
|||||||
var selectedParents = TransformGizmo.SelectedParents;
|
var selectedParents = TransformGizmo.SelectedParents;
|
||||||
if (selectedParents.Count > 0)
|
if (selectedParents.Count > 0)
|
||||||
{
|
{
|
||||||
|
// Use temporary Debug Draw context to pull any debug shapes drawing in Scene Graph Nodes - those are used in OnDebugDraw down below
|
||||||
|
if (_tempDebugDrawContext == IntPtr.Zero)
|
||||||
|
_tempDebugDrawContext = DebugDraw.AllocateContext();
|
||||||
|
DebugDraw.SetContext(_tempDebugDrawContext);
|
||||||
|
DebugDraw.UpdateContext(_tempDebugDrawContext, 1.0f);
|
||||||
|
|
||||||
for (int i = 0; i < selectedParents.Count; i++)
|
for (int i = 0; i < selectedParents.Count; i++)
|
||||||
{
|
{
|
||||||
if (selectedParents[i].IsActiveInHierarchy)
|
if (selectedParents[i].IsActiveInHierarchy)
|
||||||
selectedParents[i].OnDebugDraw(_debugDrawData);
|
selectedParents[i].OnDebugDraw(_debugDrawData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DebugDraw.SetContext(IntPtr.Zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +257,7 @@ namespace FlaxEditor.Viewport
|
|||||||
public void ShowSelectedActors()
|
public void ShowSelectedActors()
|
||||||
{
|
{
|
||||||
var orient = ViewOrientation;
|
var orient = ViewOrientation;
|
||||||
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient);
|
ViewportCamera.ShowActors(TransformGizmo.SelectedParents, ref orient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -338,7 +288,7 @@ namespace FlaxEditor.Viewport
|
|||||||
public bool SnapToGround => false;
|
public bool SnapToGround => false;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool SnapToVertex => Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Float2 MouseDelta => _mouseDelta * 1000;
|
public Float2 MouseDelta => _mouseDelta * 1000;
|
||||||
@@ -367,6 +317,13 @@ namespace FlaxEditor.Viewport
|
|||||||
_window.Spawn(actor);
|
_window.Spawn(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OpenContextMenu()
|
||||||
|
{
|
||||||
|
var mouse = PointFromWindow(Root.MousePosition);
|
||||||
|
_window.ShowContextMenu(this, ref mouse);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
|
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
|
||||||
|
|
||||||
@@ -386,151 +343,6 @@ namespace FlaxEditor.Viewport
|
|||||||
root.UpdateCallbacksToRemove.Add(_update);
|
root.UpdateCallbacksToRemove.Add(_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGizmoModeToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.ActiveMode = (TransformGizmoBase.Mode)(int)button.Tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTranslateSnappingToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRotateSnappingToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnScaleSnappingToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTransformSpaceToggle(ViewportWidgetButton button)
|
|
||||||
{
|
|
||||||
TransformGizmo.ToggleTransformSpace();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGizmoModeChanged()
|
|
||||||
{
|
|
||||||
// Update all viewport widgets status
|
|
||||||
var mode = TransformGizmo.ActiveMode;
|
|
||||||
_gizmoModeTranslate.Checked = mode == TransformGizmoBase.Mode.Translate;
|
|
||||||
_gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate;
|
|
||||||
_gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly float[] EditorViewportScaleSnapValues =
|
|
||||||
{
|
|
||||||
0.05f,
|
|
||||||
0.1f,
|
|
||||||
0.25f,
|
|
||||||
0.5f,
|
|
||||||
1.0f,
|
|
||||||
2.0f,
|
|
||||||
4.0f,
|
|
||||||
6.0f,
|
|
||||||
8.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
private void OnWidgetScaleSnapClick(ContextMenuButton button)
|
|
||||||
{
|
|
||||||
var v = (float)button.Tag;
|
|
||||||
TransformGizmo.ScaleSnapValue = v;
|
|
||||||
_scaleSnapping.Text = v.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWidgetScaleSnapShowHide(Control control)
|
|
||||||
{
|
|
||||||
if (control.Visible == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ccm = (ContextMenu)control;
|
|
||||||
foreach (var e in ccm.Items)
|
|
||||||
{
|
|
||||||
if (e is ContextMenuButton b)
|
|
||||||
{
|
|
||||||
var v = (float)b.Tag;
|
|
||||||
b.Icon = Mathf.Abs(TransformGizmo.ScaleSnapValue - v) < 0.001f
|
|
||||||
? Style.Current.CheckBoxTick
|
|
||||||
: SpriteHandle.Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly float[] EditorViewportRotateSnapValues =
|
|
||||||
{
|
|
||||||
1.0f,
|
|
||||||
5.0f,
|
|
||||||
10.0f,
|
|
||||||
15.0f,
|
|
||||||
30.0f,
|
|
||||||
45.0f,
|
|
||||||
60.0f,
|
|
||||||
90.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
private void OnWidgetRotateSnapClick(ContextMenuButton button)
|
|
||||||
{
|
|
||||||
var v = (float)button.Tag;
|
|
||||||
TransformGizmo.RotationSnapValue = v;
|
|
||||||
_rotateSnapping.Text = v.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWidgetRotateSnapShowHide(Control control)
|
|
||||||
{
|
|
||||||
if (control.Visible == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ccm = (ContextMenu)control;
|
|
||||||
foreach (var e in ccm.Items)
|
|
||||||
{
|
|
||||||
if (e is ContextMenuButton b)
|
|
||||||
{
|
|
||||||
var v = (float)b.Tag;
|
|
||||||
b.Icon = Mathf.Abs(TransformGizmo.RotationSnapValue - v) < 0.001f
|
|
||||||
? Style.Current.CheckBoxTick
|
|
||||||
: SpriteHandle.Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly float[] EditorViewportTranslateSnapValues =
|
|
||||||
{
|
|
||||||
0.1f,
|
|
||||||
0.5f,
|
|
||||||
1.0f,
|
|
||||||
5.0f,
|
|
||||||
10.0f,
|
|
||||||
100.0f,
|
|
||||||
1000.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
private void OnWidgetTranslateSnapClick(ContextMenuButton button)
|
|
||||||
{
|
|
||||||
var v = (float)button.Tag;
|
|
||||||
TransformGizmo.TranslationSnapValue = v;
|
|
||||||
_translateSnappng.Text = v.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWidgetTranslateSnapShowHide(Control control)
|
|
||||||
{
|
|
||||||
if (control.Visible == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ccm = (ContextMenu)control;
|
|
||||||
foreach (var e in ccm.Items)
|
|
||||||
{
|
|
||||||
if (e is ContextMenuButton b)
|
|
||||||
{
|
|
||||||
var v = (float)b.Tag;
|
|
||||||
b.Icon = Mathf.Abs(TransformGizmo.TranslationSnapValue - v) < 0.001f
|
|
||||||
? Style.Current.CheckBoxTick
|
|
||||||
: SpriteHandle.Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSelectionChanged()
|
private void OnSelectionChanged()
|
||||||
{
|
{
|
||||||
Gizmos.ForEach(x => x.OnSelectionChanged(_window.Selection));
|
Gizmos.ForEach(x => x.OnSelectionChanged(_window.Selection));
|
||||||
@@ -585,23 +397,6 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Draw()
|
|
||||||
{
|
|
||||||
base.Draw();
|
|
||||||
|
|
||||||
// Selected UI controls outline
|
|
||||||
for (var i = 0; i < _window.Selection.Count; i++)
|
|
||||||
{
|
|
||||||
if (_window.Selection[i]?.EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
|
|
||||||
{
|
|
||||||
var control = controlActor.Control;
|
|
||||||
var bounds = Rectangle.FromPoints(control.PointToParent(this, Float2.Zero), control.PointToParent(this, control.Size));
|
|
||||||
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnLeftMouseButtonUp()
|
protected override void OnLeftMouseButtonUp()
|
||||||
{
|
{
|
||||||
@@ -744,14 +539,7 @@ namespace FlaxEditor.Viewport
|
|||||||
/// <param name="orientation">The target view orientation.</param>
|
/// <param name="orientation">The target view orientation.</param>
|
||||||
public void FocusSelection(ref Quaternion orientation)
|
public void FocusSelection(ref Quaternion orientation)
|
||||||
{
|
{
|
||||||
if (TransformGizmo.SelectedParents.Count == 0)
|
ViewportCamera.FocusSelection(Gizmos, ref orientation);
|
||||||
return;
|
|
||||||
|
|
||||||
var gizmoBounds = Gizmos.Active.FocusBounds;
|
|
||||||
if (gizmoBounds != BoundingSphere.Empty)
|
|
||||||
((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation);
|
|
||||||
else
|
|
||||||
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -776,6 +564,13 @@ namespace FlaxEditor.Viewport
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
|
if (_tempDebugDrawContext != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
DebugDraw.FreeContext(_tempDebugDrawContext);
|
||||||
|
_tempDebugDrawContext = IntPtr.Zero;
|
||||||
|
}
|
||||||
FlaxEngine.Object.Destroy(ref SelectionOutline);
|
FlaxEngine.Object.Destroy(ref SelectionOutline);
|
||||||
FlaxEngine.Object.Destroy(ref _spritesRenderer);
|
FlaxEngine.Object.Destroy(ref _spritesRenderer);
|
||||||
|
|
||||||
@@ -799,6 +594,15 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
base.OnDebugDraw(context, ref renderContext);
|
base.OnDebugDraw(context, ref renderContext);
|
||||||
|
|
||||||
|
// Collect selected objects debug shapes again when DebugDraw is active with a custom context
|
||||||
|
_debugDrawData.Clear();
|
||||||
|
var selectedParents = TransformGizmo.SelectedParents;
|
||||||
|
for (int i = 0; i < selectedParents.Count; i++)
|
||||||
|
{
|
||||||
|
if (selectedParents[i].IsActiveInHierarchy)
|
||||||
|
selectedParents[i].OnDebugDraw(_debugDrawData);
|
||||||
|
}
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)
|
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
/// <seealso cref="FlaxEditor.Viewport.EditorViewport" />
|
/// <seealso cref="FlaxEditor.Viewport.EditorViewport" />
|
||||||
public abstract class AssetPreview : EditorViewport, IEditorPrimitivesOwner
|
public abstract class AssetPreview : EditorViewport, IEditorPrimitivesOwner
|
||||||
{
|
{
|
||||||
private ContextMenuButton _showDefaultSceneButton;
|
internal ContextMenuButton _showDefaultSceneButton;
|
||||||
private IntPtr _debugDrawContext;
|
private IntPtr _debugDrawContext;
|
||||||
private bool _debugDrawEnable;
|
private bool _debugDrawEnable;
|
||||||
private bool _editorPrimitivesEnable;
|
private bool _editorPrimitivesEnable;
|
||||||
@@ -239,6 +239,8 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
Object.Destroy(ref PreviewLight);
|
Object.Destroy(ref PreviewLight);
|
||||||
Object.Destroy(ref EnvProbe);
|
Object.Destroy(ref EnvProbe);
|
||||||
Object.Destroy(ref Sky);
|
Object.Destroy(ref Sky);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Previews
|
namespace FlaxEditor.Viewport.Previews
|
||||||
@@ -13,19 +13,11 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
/// <seealso cref="AssetPreview" />
|
/// <seealso cref="AssetPreview" />
|
||||||
public class PrefabPreview : AssetPreview
|
public class PrefabPreview : AssetPreview
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The currently spawned prefab instance owner. Used to link some actors such as UIControl to preview scene and view.
|
|
||||||
/// </summary>
|
|
||||||
internal static PrefabPreview LoadingPreview;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of active prefab previews. Used to link some actors such as UIControl to preview scene and view.
|
|
||||||
/// </summary>
|
|
||||||
internal static List<PrefabPreview> ActivePreviews;
|
|
||||||
|
|
||||||
private Prefab _prefab;
|
private Prefab _prefab;
|
||||||
private Actor _instance;
|
private Actor _instance;
|
||||||
internal UIControl customControlLinked;
|
private UIControl _uiControlLinked;
|
||||||
|
internal bool _hasUILinked;
|
||||||
|
internal ContainerControl _uiParentLink;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the prefab asset to preview.
|
/// Gets or sets the prefab asset to preview.
|
||||||
@@ -54,13 +46,10 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
_prefab.WaitForLoaded();
|
_prefab.WaitForLoaded();
|
||||||
|
|
||||||
// Spawn prefab
|
// Spawn prefab
|
||||||
LoadingPreview = this;
|
|
||||||
var instance = PrefabManager.SpawnPrefab(_prefab, null);
|
var instance = PrefabManager.SpawnPrefab(_prefab, null);
|
||||||
LoadingPreview = null;
|
|
||||||
if (instance == null)
|
if (instance == null)
|
||||||
{
|
{
|
||||||
_prefab = null;
|
_prefab = null;
|
||||||
ActivePreviews.Remove(this);
|
|
||||||
throw new Exception("Failed to spawn a prefab for the preview.");
|
throw new Exception("Failed to spawn a prefab for the preview.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,11 +73,11 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
if (_instance)
|
if (_instance)
|
||||||
{
|
{
|
||||||
// Unlink UI control
|
// Unlink UI control
|
||||||
if (customControlLinked)
|
if (_uiControlLinked)
|
||||||
{
|
{
|
||||||
if (customControlLinked.Control?.Parent == this)
|
if (_uiControlLinked.Control?.Parent == _uiParentLink)
|
||||||
customControlLinked.Control.Parent = null;
|
_uiControlLinked.Control.Parent = null;
|
||||||
customControlLinked = null;
|
_uiControlLinked = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove for the preview
|
// Remove for the preview
|
||||||
@@ -96,27 +85,51 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
|
|
||||||
_instance = value;
|
_instance = value;
|
||||||
|
_hasUILinked = false;
|
||||||
|
|
||||||
if (_instance)
|
if (_instance)
|
||||||
{
|
{
|
||||||
// Add to the preview
|
// Add to the preview
|
||||||
Task.AddCustomActor(_instance);
|
Task.AddCustomActor(_instance);
|
||||||
|
UpdateLinkage();
|
||||||
// Link UI canvases to the preview
|
|
||||||
LinkCanvas(_instance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateLinkage()
|
||||||
|
{
|
||||||
|
// Clear flag
|
||||||
|
_hasUILinked = false;
|
||||||
|
|
||||||
|
// Link UI canvases to the preview (eg. after canvas added to the prefab)
|
||||||
|
LinkCanvas(_instance);
|
||||||
|
|
||||||
|
// Link UI control to the preview
|
||||||
|
if (_uiControlLinked == null &&
|
||||||
|
_instance is UIControl uiControl &&
|
||||||
|
uiControl.Control != null &&
|
||||||
|
uiControl.Control.Parent == null)
|
||||||
|
{
|
||||||
|
uiControl.Control.Parent = _uiParentLink;
|
||||||
|
_uiControlLinked = uiControl;
|
||||||
|
_hasUILinked = true;
|
||||||
|
}
|
||||||
|
else if (_uiControlLinked != null)
|
||||||
|
_hasUILinked = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void LinkCanvas(Actor actor)
|
private void LinkCanvas(Actor actor)
|
||||||
{
|
{
|
||||||
if (actor is UICanvas uiCanvas)
|
if (actor is UICanvas uiCanvas)
|
||||||
uiCanvas.EditorOverride(Task, this);
|
{
|
||||||
|
uiCanvas.EditorOverride(Task, _uiParentLink);
|
||||||
|
if (uiCanvas.GUI.Parent == _uiParentLink)
|
||||||
|
_hasUILinked = true;
|
||||||
|
}
|
||||||
|
|
||||||
var children = actor.ChildrenCount;
|
var children = actor.ChildrenCount;
|
||||||
for (int i = 0; i < children; i++)
|
for (int i = 0; i < children; i++)
|
||||||
{
|
|
||||||
LinkCanvas(actor.GetChild(i));
|
LinkCanvas(actor.GetChild(i));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -126,9 +139,8 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
public PrefabPreview(bool useWidgets)
|
public PrefabPreview(bool useWidgets)
|
||||||
: base(useWidgets)
|
: base(useWidgets)
|
||||||
{
|
{
|
||||||
if (ActivePreviews == null)
|
// Link to itself by default
|
||||||
ActivePreviews = new List<PrefabPreview>();
|
_uiParentLink = this;
|
||||||
ActivePreviews.Add(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -138,15 +150,13 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
|
|
||||||
if (_instance != null)
|
if (_instance != null)
|
||||||
{
|
{
|
||||||
// Link UI canvases to the preview (eg. after canvas added to the prefab)
|
UpdateLinkage();
|
||||||
LinkCanvas(_instance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
ActivePreviews.Remove(this);
|
|
||||||
Prefab = null;
|
Prefab = null;
|
||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace FlaxEditor.Viewport
|
|||||||
var gridPlane = new Plane(Vector3.Zero, Vector3.Up);
|
var gridPlane = new Plane(Vector3.Zero, Vector3.Up);
|
||||||
var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives;
|
var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives;
|
||||||
hit = _owner.SceneGraphRoot.RayCast(ref ray, ref view, out var closest, out var normal, flags);
|
hit = _owner.SceneGraphRoot.RayCast(ref ray, ref view, out var closest, out var normal, flags);
|
||||||
var girdGizmo = (GridGizmo)_owner.Gizmos.FirstOrDefault(x => x is GridGizmo);
|
var girdGizmo = _owner.Gizmos.Get<GridGizmo>();
|
||||||
if (hit != null)
|
if (hit != null)
|
||||||
{
|
{
|
||||||
// Use hit location
|
// Use hit location
|
||||||
@@ -180,7 +180,7 @@ namespace FlaxEditor.Viewport
|
|||||||
var location = hitLocation + new Vector3(0, bottomToCenter, 0);
|
var location = hitLocation + new Vector3(0, bottomToCenter, 0);
|
||||||
|
|
||||||
// Apply grid snapping if enabled
|
// Apply grid snapping if enabled
|
||||||
var transformGizmo = (TransformGizmo)_owner.Gizmos.FirstOrDefault(x => x is TransformGizmo);
|
var transformGizmo = _owner.Gizmos.Get<TransformGizmo>();
|
||||||
if (transformGizmo != null && (_owner.UseSnapping || transformGizmo.TranslationSnapEnable))
|
if (transformGizmo != null && (_owner.UseSnapping || transformGizmo.TranslationSnapEnable))
|
||||||
{
|
{
|
||||||
float snapValue = transformGizmo.TranslationSnapValue;
|
float snapValue = transformGizmo.TranslationSnapValue;
|
||||||
|
|||||||
@@ -186,6 +186,10 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
base.Initialize(layout);
|
base.Initialize(layout);
|
||||||
|
|
||||||
|
// Ignore import settings GUI if the type is not animation. This removes the import UI if the animation asset was not created using an import.
|
||||||
|
if (proxy.ImportSettings.Settings.Type != FlaxEngine.Tools.ModelTool.ModelType.Animation)
|
||||||
|
return;
|
||||||
|
|
||||||
// Import Settings
|
// Import Settings
|
||||||
{
|
{
|
||||||
var group = layout.Group("Import Settings");
|
var group = layout.Group("Import Settings");
|
||||||
|
|||||||
@@ -76,37 +76,37 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
// Transparency
|
// Transparency
|
||||||
|
|
||||||
[EditorOrder(200), DefaultValue(MaterialTransparentLightingMode.Surface), EditorDisplay("Transparency"), Tooltip("Transparent material lighting mode.")]
|
[EditorOrder(200), DefaultValue(MaterialTransparentLightingMode.Surface), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Transparent material lighting mode.")]
|
||||||
public MaterialTransparentLightingMode TransparentLightingMode;
|
public MaterialTransparentLightingMode TransparentLightingMode;
|
||||||
|
|
||||||
[EditorOrder(205), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")]
|
[EditorOrder(205), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")]
|
||||||
public bool EnableReflections;
|
public bool EnableReflections;
|
||||||
|
|
||||||
[VisibleIf(nameof(EnableReflections))]
|
[VisibleIf(nameof(EnableReflections))]
|
||||||
[EditorOrder(210), DefaultValue(false), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")]
|
[EditorOrder(210), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")]
|
||||||
public bool EnableScreenSpaceReflections;
|
public bool EnableScreenSpaceReflections;
|
||||||
|
|
||||||
[EditorOrder(210), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")]
|
[EditorOrder(210), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")]
|
||||||
public bool EnableFog;
|
public bool EnableFog;
|
||||||
|
|
||||||
[EditorOrder(220), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables distortion effect when rendering.")]
|
[EditorOrder(220), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables distortion effect when rendering.")]
|
||||||
public bool EnableDistortion;
|
public bool EnableDistortion;
|
||||||
|
|
||||||
[EditorOrder(224), DefaultValue(false), EditorDisplay("Transparency"), Tooltip("Enables sampling Global Illumination in material (eg. light probes or volumetric lightmap).")]
|
[EditorOrder(224), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables sampling Global Illumination in material (eg. light probes or volumetric lightmap).")]
|
||||||
public bool EnableGlobalIllumination;
|
public bool EnableGlobalIllumination;
|
||||||
|
|
||||||
[EditorOrder(225), DefaultValue(false), EditorDisplay("Transparency"), Tooltip("Enables refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.")]
|
[EditorOrder(225), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.")]
|
||||||
public bool PixelNormalOffsetRefraction;
|
public bool PixelNormalOffsetRefraction;
|
||||||
|
|
||||||
[EditorOrder(230), DefaultValue(0.12f), EditorDisplay("Transparency"), Tooltip("Controls opacity values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
[EditorOrder(230), DefaultValue(0.12f), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Controls opacity values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
||||||
public float OpacityThreshold;
|
public float OpacityThreshold;
|
||||||
|
|
||||||
// Tessellation
|
// Tessellation
|
||||||
|
|
||||||
[EditorOrder(300), DefaultValue(TessellationMethod.None), EditorDisplay("Tessellation"), Tooltip("Mesh tessellation method.")]
|
[EditorOrder(300), DefaultValue(TessellationMethod.None), VisibleIf(nameof(IsStandard)), EditorDisplay("Tessellation"), Tooltip("Mesh tessellation method.")]
|
||||||
public TessellationMethod TessellationMode;
|
public TessellationMethod TessellationMode;
|
||||||
|
|
||||||
[EditorOrder(310), DefaultValue(15), EditorDisplay("Tessellation"), Tooltip("Maximum triangle tessellation factor."), Limit(1, 60, 0.01f)]
|
[EditorOrder(310), DefaultValue(15), VisibleIf(nameof(IsStandard)), EditorDisplay("Tessellation"), Tooltip("Maximum triangle tessellation factor."), Limit(1, 60, 0.01f)]
|
||||||
public int MaxTessellationFactor;
|
public int MaxTessellationFactor;
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
@@ -120,10 +120,10 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
[EditorOrder(420), DefaultValue(0.3f), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
[EditorOrder(420), DefaultValue(0.3f), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
||||||
public float MaskThreshold;
|
public float MaskThreshold;
|
||||||
|
|
||||||
[EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")]
|
[EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), VisibleIf(nameof(IsDecal)), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")]
|
||||||
public MaterialDecalBlendingMode DecalBlendingMode;
|
public MaterialDecalBlendingMode DecalBlendingMode;
|
||||||
|
|
||||||
[EditorOrder(440), DefaultValue(MaterialPostFxLocation.AfterPostProcessingPass), EditorDisplay("Misc"), Tooltip("The post fx material rendering location.")]
|
[EditorOrder(440), DefaultValue(MaterialPostFxLocation.AfterPostProcessingPass), VisibleIf(nameof(IsPostProcess)), EditorDisplay("Misc"), Tooltip("The post fx material rendering location.")]
|
||||||
public MaterialPostFxLocation PostFxLocation;
|
public MaterialPostFxLocation PostFxLocation;
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
@@ -140,6 +140,12 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
set => throw new Exception("No setter.");
|
set => throw new Exception("No setter.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Visibility conditionals
|
||||||
|
|
||||||
|
private bool IsPostProcess => Domain == MaterialDomain.PostProcess;
|
||||||
|
private bool IsDecal => Domain == MaterialDomain.Decal;
|
||||||
|
private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gathers parameters from the specified material.
|
/// Gathers parameters from the specified material.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -360,10 +360,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parent">The parent control.</param>
|
/// <param name="parent">The parent control.</param>
|
||||||
/// <param name="location">The location (within a given control).</param>
|
/// <param name="location">The location (within a given control).</param>
|
||||||
private void ShowContextMenu(Control parent, ref Float2 location)
|
internal void ShowContextMenu(Control parent, ref Float2 location)
|
||||||
{
|
{
|
||||||
var contextMenu = CreateContextMenu();
|
var contextMenu = CreateContextMenu();
|
||||||
|
|
||||||
contextMenu.Show(parent, location);
|
contextMenu.Show(parent, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.Gizmo;
|
|
||||||
using FlaxEditor.GUI.Tree;
|
using FlaxEditor.GUI.Tree;
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.SceneGraph.GUI;
|
using FlaxEditor.SceneGraph.GUI;
|
||||||
using FlaxEditor.Viewport.Cameras;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.Windows.Assets
|
namespace FlaxEditor.Windows.Assets
|
||||||
@@ -64,8 +62,11 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
private void OnSelectionUndo(SceneGraphNode[] toSelect)
|
private void OnSelectionUndo(SceneGraphNode[] toSelect)
|
||||||
{
|
{
|
||||||
Selection.Clear();
|
Selection.Clear();
|
||||||
Selection.AddRange(toSelect);
|
foreach (var e in toSelect)
|
||||||
|
{
|
||||||
|
if (e != null)
|
||||||
|
Selection.Add(e);
|
||||||
|
}
|
||||||
OnSelectionChanges();
|
OnSelectionChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,11 +119,13 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// <param name="nodes">The nodes.</param>
|
/// <param name="nodes">The nodes.</param>
|
||||||
public void Select(List<SceneGraphNode> nodes)
|
public void Select(List<SceneGraphNode> nodes)
|
||||||
{
|
{
|
||||||
|
nodes?.RemoveAll(x => x == null);
|
||||||
if (nodes == null || nodes.Count == 0)
|
if (nodes == null || nodes.Count == 0)
|
||||||
{
|
{
|
||||||
Deselect();
|
Deselect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils.ArraysEqual(Selection, nodes))
|
if (Utils.ArraysEqual(Selection, nodes))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
IsScrollable = false,
|
IsScrollable = false,
|
||||||
Offsets = new Margin(0, 0, 0, 18 + 6),
|
Offsets = new Margin(0, 0, 0, 18 + 6),
|
||||||
};
|
};
|
||||||
_searchBox = new SearchBox()
|
_searchBox = new SearchBox
|
||||||
{
|
{
|
||||||
AnchorPreset = AnchorPresets.HorizontalStretchMiddle,
|
AnchorPreset = AnchorPresets.HorizontalStretchMiddle,
|
||||||
Parent = headerPanel,
|
Parent = headerPanel,
|
||||||
@@ -140,7 +140,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
};
|
};
|
||||||
_searchBox.TextChanged += OnSearchBoxTextChanged;
|
_searchBox.TextChanged += OnSearchBoxTextChanged;
|
||||||
|
|
||||||
_treePanel = new Panel()
|
// Prefab structure tree
|
||||||
|
_treePanel = new Panel
|
||||||
{
|
{
|
||||||
AnchorPreset = AnchorPresets.StretchAll,
|
AnchorPreset = AnchorPresets.StretchAll,
|
||||||
Offsets = new Margin(0.0f, 0.0f, headerPanel.Bottom, 0.0f),
|
Offsets = new Margin(0.0f, 0.0f, headerPanel.Bottom, 0.0f),
|
||||||
@@ -148,8 +149,6 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
IsScrollable = true,
|
IsScrollable = true,
|
||||||
Parent = sceneTreePanel,
|
Parent = sceneTreePanel,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prefab structure tree
|
|
||||||
Graph = new LocalSceneGraph(new CustomRootNode(this));
|
Graph = new LocalSceneGraph(new CustomRootNode(this));
|
||||||
Graph.Root.TreeNode.Expand(true);
|
Graph.Root.TreeNode.Expand(true);
|
||||||
_tree = new PrefabTree
|
_tree = new PrefabTree
|
||||||
@@ -316,11 +315,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Restore
|
// Restore
|
||||||
_viewport.Prefab = _asset;
|
OnPrefabOpened();
|
||||||
Graph.MainActor = _viewport.Instance;
|
|
||||||
Selection.Clear();
|
|
||||||
Select(Graph.Main);
|
|
||||||
Graph.Root.TreeNode.Expand(true);
|
|
||||||
_undo.Clear();
|
_undo.Clear();
|
||||||
ClearEditedFlag();
|
ClearEditedFlag();
|
||||||
}
|
}
|
||||||
@@ -346,6 +341,16 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnPrefabOpened()
|
||||||
|
{
|
||||||
|
_viewport.Prefab = _asset;
|
||||||
|
_viewport.UpdateGizmoMode();
|
||||||
|
Graph.MainActor = _viewport.Instance;
|
||||||
|
Selection.Clear();
|
||||||
|
Select(Graph.Main);
|
||||||
|
Graph.Root.TreeNode.Expand(true);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Save()
|
public override void Save()
|
||||||
{
|
{
|
||||||
@@ -355,6 +360,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Editor.Scene.OnSaveStart(_viewport._uiParentLink);
|
||||||
|
|
||||||
// Simply update changes
|
// Simply update changes
|
||||||
Editor.Prefabs.ApplyAll(_viewport.Instance);
|
Editor.Prefabs.ApplyAll(_viewport.Instance);
|
||||||
|
|
||||||
@@ -371,6 +378,10 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Editor.Scene.OnSaveEnd(_viewport._uiParentLink);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -411,13 +422,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewport.Prefab = _asset;
|
OnPrefabOpened();
|
||||||
Graph.MainActor = _viewport.Instance;
|
|
||||||
_focusCamera = true;
|
_focusCamera = true;
|
||||||
Selection.Clear();
|
|
||||||
Select(Graph.Main);
|
|
||||||
Graph.Root.TreeNode.Expand(true);
|
|
||||||
|
|
||||||
_undo.Clear();
|
_undo.Clear();
|
||||||
ClearEditedFlag();
|
ClearEditedFlag();
|
||||||
|
|
||||||
@@ -462,11 +468,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_viewport.Prefab = null;
|
_viewport.Prefab = null;
|
||||||
if (_asset.IsLoaded)
|
if (_asset.IsLoaded)
|
||||||
{
|
{
|
||||||
_viewport.Prefab = _asset;
|
OnPrefabOpened();
|
||||||
Graph.MainActor = _viewport.Instance;
|
|
||||||
Selection.Clear();
|
|
||||||
Select(Graph.Main);
|
|
||||||
Graph.Root.TreeNode.ExpandAll(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -478,7 +480,6 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
if (_focusCamera && _viewport.Task.FrameCount > 1)
|
if (_focusCamera && _viewport.Task.FrameCount > 1)
|
||||||
{
|
{
|
||||||
_focusCamera = false;
|
_focusCamera = false;
|
||||||
|
|
||||||
Editor.GetActorEditorSphere(_viewport.Instance, out BoundingSphere bounds);
|
Editor.GetActorEditorSphere(_viewport.Instance, out BoundingSphere bounds);
|
||||||
_viewport.ViewPosition = bounds.Center - _viewport.ViewDirection * (bounds.Radius * 1.2f);
|
_viewport.ViewPosition = bounds.Center - _viewport.ViewDirection * (bounds.Radius * 1.2f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,6 +281,9 @@ namespace FlaxEditor.Windows
|
|||||||
_view.OnDelete += Delete;
|
_view.OnDelete += Delete;
|
||||||
_view.OnDuplicate += Duplicate;
|
_view.OnDuplicate += Duplicate;
|
||||||
_view.OnPaste += Paste;
|
_view.OnPaste += Paste;
|
||||||
|
|
||||||
|
_view.InputActions.Add(options => options.Search, () => _itemsSearchBox.Focus());
|
||||||
|
InputActions.Add(options => options.Search, () => _itemsSearchBox.Focus());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox)
|
private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace FlaxEditor.Windows
|
|||||||
private Tabs _tabs;
|
private Tabs _tabs;
|
||||||
private EditorOptions _options;
|
private EditorOptions _options;
|
||||||
private ToolStripButton _saveButton;
|
private ToolStripButton _saveButton;
|
||||||
|
private readonly Undo _undo;
|
||||||
private readonly List<Tab> _customTabs = new List<Tab>();
|
private readonly List<Tab> _customTabs = new List<Tab>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -33,6 +34,12 @@ namespace FlaxEditor.Windows
|
|||||||
: base(editor, true, ScrollBars.None)
|
: base(editor, true, ScrollBars.None)
|
||||||
{
|
{
|
||||||
Title = "Editor Options";
|
Title = "Editor Options";
|
||||||
|
|
||||||
|
// Undo
|
||||||
|
_undo = new Undo();
|
||||||
|
_undo.UndoDone += OnUndoRedo;
|
||||||
|
_undo.RedoDone += OnUndoRedo;
|
||||||
|
_undo.ActionDone += OnUndoRedo;
|
||||||
|
|
||||||
var toolstrip = new ToolStrip
|
var toolstrip = new ToolStrip
|
||||||
{
|
{
|
||||||
@@ -58,9 +65,19 @@ namespace FlaxEditor.Windows
|
|||||||
CreateTab("Visual", () => _options.Visual);
|
CreateTab("Visual", () => _options.Visual);
|
||||||
CreateTab("Source Code", () => _options.SourceCode);
|
CreateTab("Source Code", () => _options.SourceCode);
|
||||||
CreateTab("Theme", () => _options.Theme);
|
CreateTab("Theme", () => _options.Theme);
|
||||||
|
|
||||||
|
// Setup input actions
|
||||||
|
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
||||||
|
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
||||||
|
InputActions.Add(options => options.Save, SaveData);
|
||||||
|
|
||||||
_tabs.SelectedTabIndex = 0;
|
_tabs.SelectedTabIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnUndoRedo(IUndoAction action)
|
||||||
|
{
|
||||||
|
MarkAsEdited();
|
||||||
|
}
|
||||||
|
|
||||||
private Tab CreateTab(string name, Func<object> getValue)
|
private Tab CreateTab(string name, Func<object> getValue)
|
||||||
{
|
{
|
||||||
@@ -73,7 +90,7 @@ namespace FlaxEditor.Windows
|
|||||||
Parent = tab
|
Parent = tab
|
||||||
};
|
};
|
||||||
|
|
||||||
var settings = new CustomEditorPresenter(null);
|
var settings = new CustomEditorPresenter(_undo);
|
||||||
settings.Panel.Parent = panel;
|
settings.Panel.Parent = panel;
|
||||||
settings.Panel.Tag = getValue;
|
settings.Panel.Tag = getValue;
|
||||||
settings.Modified += MarkAsEdited;
|
settings.Modified += MarkAsEdited;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace FlaxEditor.Windows
|
|||||||
protected EditorWindow(Editor editor, bool hideOnClose, ScrollBars scrollBars)
|
protected EditorWindow(Editor editor, bool hideOnClose, ScrollBars scrollBars)
|
||||||
: base(editor.UI.MasterPanel, hideOnClose, scrollBars)
|
: base(editor.UI.MasterPanel, hideOnClose, scrollBars)
|
||||||
{
|
{
|
||||||
|
AutoFocus = true;
|
||||||
Editor = editor;
|
Editor = editor;
|
||||||
|
|
||||||
InputActions.Add(options => options.ContentFinder, () =>
|
InputActions.Add(options => options.ContentFinder, () =>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using FlaxEditor.Gizmo;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.GUI.Input;
|
using FlaxEditor.GUI.Input;
|
||||||
using FlaxEditor.Options;
|
using FlaxEditor.Options;
|
||||||
@@ -194,133 +195,14 @@ namespace FlaxEditor.Windows
|
|||||||
public bool Active;
|
public bool Active;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GameRoot : ContainerControl
|
/// <summary>
|
||||||
|
/// Root control for game UI preview in Editor. Supports basic UI editing via <see cref="UIEditorRoot"/>.
|
||||||
|
/// </summary>
|
||||||
|
private class GameRoot : UIEditorRoot
|
||||||
{
|
{
|
||||||
public bool EnableEvents => !Time.GamePaused;
|
public override bool EnableInputs => !Time.GamePaused && Editor.IsPlayMode;
|
||||||
|
public override bool EnableSelecting => !Editor.IsPlayMode || Time.GamePaused;
|
||||||
public override bool RayCast(ref Float2 location, out Control hit)
|
public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo;
|
||||||
{
|
|
||||||
return RayCastChildren(ref location, out hit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool ContainsPoint(ref Float2 location, bool precise = false)
|
|
||||||
{
|
|
||||||
if (precise)
|
|
||||||
return false;
|
|
||||||
return base.ContainsPoint(ref location, precise);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnCharInput(char c)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return base.OnCharInput(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return DragDropEffect.None;
|
|
||||||
|
|
||||||
return base.OnDragDrop(ref location, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return DragDropEffect.None;
|
|
||||||
|
|
||||||
return base.OnDragEnter(ref location, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnDragLeave()
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return;
|
|
||||||
|
|
||||||
base.OnDragLeave();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return DragDropEffect.None;
|
|
||||||
|
|
||||||
return base.OnDragMove(ref location, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnKeyDown(KeyboardKeys key)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return base.OnKeyDown(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnKeyUp(KeyboardKeys key)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return;
|
|
||||||
|
|
||||||
base.OnKeyUp(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return base.OnMouseDoubleClick(location, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return base.OnMouseDown(location, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnMouseEnter(Float2 location)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return;
|
|
||||||
|
|
||||||
base.OnMouseEnter(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnMouseLeave()
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return;
|
|
||||||
|
|
||||||
base.OnMouseLeave();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnMouseMove(Float2 location)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return;
|
|
||||||
|
|
||||||
base.OnMouseMove(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return base.OnMouseUp(location, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnMouseWheel(Float2 location, float delta)
|
|
||||||
{
|
|
||||||
if (!EnableEvents)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return base.OnMouseWheel(location, delta);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -348,13 +230,9 @@ namespace FlaxEditor.Windows
|
|||||||
// Override the game GUI root
|
// Override the game GUI root
|
||||||
_guiRoot = new GameRoot
|
_guiRoot = new GameRoot
|
||||||
{
|
{
|
||||||
AnchorPreset = AnchorPresets.StretchAll,
|
|
||||||
Offsets = Margin.Zero,
|
|
||||||
//Visible = false,
|
|
||||||
AutoFocus = false,
|
|
||||||
Parent = _viewport
|
Parent = _viewport
|
||||||
};
|
};
|
||||||
RootControl.GameRoot = _guiRoot;
|
RootControl.GameRoot = _guiRoot.UIRoot;
|
||||||
|
|
||||||
SizeChanged += control => { ResizeViewport(); };
|
SizeChanged += control => { ResizeViewport(); };
|
||||||
|
|
||||||
@@ -382,6 +260,56 @@ namespace FlaxEditor.Windows
|
|||||||
Editor.Instance.Windows.ProfilerWin.Clear();
|
Editor.Instance.Windows.ProfilerWin.Clear();
|
||||||
Editor.Instance.UI.AddStatusMessage($"Profiling results cleared.");
|
Editor.Instance.UI.AddStatusMessage($"Profiling results cleared.");
|
||||||
});
|
});
|
||||||
|
InputActions.Add(options => options.Save, () =>
|
||||||
|
{
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
return;
|
||||||
|
Editor.Instance.SaveAll();
|
||||||
|
});
|
||||||
|
InputActions.Add(options => options.Undo, () =>
|
||||||
|
{
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
return;
|
||||||
|
Editor.Instance.PerformUndo();
|
||||||
|
Focus();
|
||||||
|
});
|
||||||
|
InputActions.Add(options => options.Redo, () =>
|
||||||
|
{
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
return;
|
||||||
|
Editor.Instance.PerformRedo();
|
||||||
|
Focus();
|
||||||
|
});
|
||||||
|
InputActions.Add(options => options.Cut, () =>
|
||||||
|
{
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
return;
|
||||||
|
Editor.Instance.SceneEditing.Cut();
|
||||||
|
});
|
||||||
|
InputActions.Add(options => options.Copy, () =>
|
||||||
|
{
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
return;
|
||||||
|
Editor.Instance.SceneEditing.Copy();
|
||||||
|
});
|
||||||
|
InputActions.Add(options => options.Paste, () =>
|
||||||
|
{
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
return;
|
||||||
|
Editor.Instance.SceneEditing.Paste();
|
||||||
|
});
|
||||||
|
InputActions.Add(options => options.Duplicate, () =>
|
||||||
|
{
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
return;
|
||||||
|
Editor.Instance.SceneEditing.Duplicate();
|
||||||
|
});
|
||||||
|
InputActions.Add(options => options.Delete, () =>
|
||||||
|
{
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
return;
|
||||||
|
Editor.Instance.SceneEditing.Delete();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeViewportRatio(ViewportScaleOptions v)
|
private void ChangeViewportRatio(ViewportScaleOptions v)
|
||||||
@@ -916,35 +844,6 @@ namespace FlaxEditor.Windows
|
|||||||
Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selected UI controls outline
|
|
||||||
bool drawAnySelectedControl = false;
|
|
||||||
// TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed)
|
|
||||||
var selection = Editor.SceneEditing.Selection;
|
|
||||||
for (var i = 0; i < selection.Count; i++)
|
|
||||||
{
|
|
||||||
if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
|
|
||||||
{
|
|
||||||
if (!drawAnySelectedControl)
|
|
||||||
{
|
|
||||||
drawAnySelectedControl = true;
|
|
||||||
Render2D.PushTransform(ref _viewport._cachedTransform);
|
|
||||||
}
|
|
||||||
var options = Editor.Options.Options.Visual;
|
|
||||||
var control = controlActor.Control;
|
|
||||||
var bounds = control.EditorBounds;
|
|
||||||
var p1 = control.PointToParent(_viewport, bounds.UpperLeft);
|
|
||||||
var p2 = control.PointToParent(_viewport, bounds.UpperRight);
|
|
||||||
var p3 = control.PointToParent(_viewport, bounds.BottomLeft);
|
|
||||||
var p4 = control.PointToParent(_viewport, bounds.BottomRight);
|
|
||||||
var min = Float2.Min(Float2.Min(p1, p2), Float2.Min(p3, p4));
|
|
||||||
var max = Float2.Max(Float2.Max(p1, p2), Float2.Max(p3, p4));
|
|
||||||
bounds = new Rectangle(min, Float2.Max(max - min, Float2.Zero));
|
|
||||||
Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (drawAnySelectedControl)
|
|
||||||
Render2D.PopTransform();
|
|
||||||
|
|
||||||
// Play mode hints and overlay
|
// Play mode hints and overlay
|
||||||
if (Editor.StateMachine.IsPlayMode)
|
if (Editor.StateMachine.IsPlayMode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ namespace FlaxEditor.Windows
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public OutputLogWindow Window;
|
public OutputLogWindow Window;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The input actions collection to processed during user input.
|
||||||
|
/// </summary>
|
||||||
|
public InputActionsContainer InputActions = new InputActionsContainer();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default text style.
|
/// The default text style.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -80,6 +85,14 @@ namespace FlaxEditor.Windows
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TextBlockStyle ErrorStyle;
|
public TextBlockStyle ErrorStyle;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
|
{
|
||||||
|
if (InputActions.Process(Editor.Instance, this, key))
|
||||||
|
return true;
|
||||||
|
return base.OnKeyDown(key);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnParseTextBlocks()
|
protected override void OnParseTextBlocks()
|
||||||
{
|
{
|
||||||
@@ -201,6 +214,9 @@ namespace FlaxEditor.Windows
|
|||||||
// Setup editor options
|
// Setup editor options
|
||||||
Editor.Options.OptionsChanged += OnEditorOptionsChanged;
|
Editor.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||||
OnEditorOptionsChanged(Editor.Options.Options);
|
OnEditorOptionsChanged(Editor.Options.Options);
|
||||||
|
|
||||||
|
_output.InputActions.Add(options => options.Search, () => _searchBox.Focus());
|
||||||
|
InputActions.Add(options => options.Search, () => _searchBox.Focus());
|
||||||
|
|
||||||
GameCooker.Event += OnGameCookerEvent;
|
GameCooker.Event += OnGameCookerEvent;
|
||||||
ScriptsBuilder.CompilationFailed += OnScriptsCompilationFailed;
|
ScriptsBuilder.CompilationFailed += OnScriptsCompilationFailed;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user