Compare commits
153 Commits
56375e752b
...
fe313773d1
| Author | SHA1 | Date | |
|---|---|---|---|
| fe313773d1 | |||
| 34346e4111 | |||
| 7cbe44c4e5 | |||
| 846f44b882 | |||
| 68764580f2 | |||
|
|
cdf0e24c12 | ||
| ac7087c4ce | |||
| 56e6e53d0c | |||
| 25b6605027 | |||
| c97f6f3b7e | |||
| bc33d1a0f5 | |||
| 5e950833ff | |||
| 9a04cee71f | |||
| b79c9402fd | |||
| b0aed96d18 | |||
| c003506906 | |||
| 473dc4a1a0 | |||
| 8d97e21b28 | |||
| ce932b1739 | |||
| 506ad85cad | |||
| e86ccefd3c | |||
| 71147cd4e9 | |||
| d6fe1f0519 | |||
| ed3d1ce366 | |||
| 37177edeaa | |||
| 7981483a5b | |||
| c3ca359b4b | |||
| 99cf7292aa | |||
| 4c6f39d489 | |||
| dcfc265433 | |||
| 4a7f6c34f3 | |||
| ba248a61bd | |||
| eacd23bdbb | |||
| 62df728452 | |||
| fe0d97aad1 | |||
| 7f7d839f23 | |||
| c445bfd5d4 | |||
| 62843476b7 | |||
| 7595c572d7 | |||
| 13ae091273 | |||
| a9dccd2949 | |||
| f8dd3f23c6 | |||
|
|
0f847335c3 | ||
|
|
12f3f00f9f | ||
|
|
2175837f33 | ||
|
|
1e2493d4ff | ||
|
|
622de6ebcb | ||
|
|
4de324f2b0 | ||
|
|
01bd482ba7 | ||
|
|
cedacdba3e | ||
|
|
06dcc7ba8e | ||
|
|
ff0840d6dd | ||
|
|
b81435505d | ||
|
|
526010f523 | ||
|
|
222a614a2b | ||
|
|
bd4a042619 | ||
|
|
3e9048af2f | ||
|
|
0a2341674f | ||
|
|
bb844a7606 | ||
|
|
ef60cc30ff | ||
|
|
f8bb17a945 | ||
|
|
9ad1a9bfd0 | ||
|
|
8ec279c1dd | ||
|
|
bbed25653a | ||
|
|
902d14c334 | ||
|
|
fb44f0458c | ||
|
|
fa8f50ead1 | ||
|
|
f57f57423c | ||
|
|
c6b7077c2d | ||
|
|
21d1419e74 | ||
|
|
696b1c0d0c | ||
|
|
7e0c0559dd | ||
|
|
a0216746b9 | ||
|
|
76b84cca4e | ||
|
|
a780f513db | ||
|
|
0906a561c4 | ||
|
|
99e836b1fb | ||
|
|
1e61abdfef | ||
|
|
4a2f4a98ec | ||
|
|
24266b64da | ||
|
|
139a431614 | ||
|
|
4f281b4732 | ||
|
|
4fe46dfc83 | ||
|
|
04a3435200 | ||
|
|
84c65b92d0 | ||
|
|
5ffc06d6ee | ||
|
|
23015009b3 | ||
|
|
09414f9002 | ||
|
|
3e5cb09381 | ||
|
|
eb5dfcd6bf | ||
|
|
6a082e9dd7 | ||
|
|
667661dd90 | ||
|
|
3e344e789a | ||
|
|
e9243d0358 | ||
|
|
82453acf53 | ||
|
|
0cf31395b5 | ||
|
|
ce23c2efaf | ||
|
|
b6d2a3683c | ||
|
|
1088a71e69 | ||
|
|
31e870b086 | ||
|
|
2f239fe405 | ||
|
|
64a674f9bf | ||
|
|
e53b2b5736 | ||
|
|
a2087297e0 | ||
|
|
ca15318ade | ||
|
|
47959ac901 | ||
|
|
56e6176e9d | ||
|
|
6af46bb764 | ||
|
|
6f00d664bb | ||
|
|
2441a35611 | ||
|
|
cee0b24b9f | ||
|
|
66cc3196e1 | ||
|
|
76518ac051 | ||
|
|
3aed3f1954 | ||
|
|
0ef1220846 | ||
|
|
987916cc1c | ||
|
|
4ac334acac | ||
|
|
2230f907fd | ||
|
|
6fabd0c26d | ||
|
|
24a9ec5dd5 | ||
|
|
2328096200 | ||
|
|
dcce8581c5 | ||
|
|
992d907b9c | ||
|
|
93fefc9af3 | ||
|
|
e27d18ef87 | ||
|
|
848dbdf532 | ||
|
|
2f16694529 | ||
|
|
12d9d94138 | ||
|
|
c01824cd09 | ||
|
|
10caaf4fe9 | ||
|
|
81737083a0 | ||
|
|
57628c3d5f | ||
|
|
0a4a431f74 | ||
|
|
7e2d45012e | ||
|
|
f0f631a48b | ||
|
|
0295e1ca90 | ||
|
|
8ef7f7cb1a | ||
|
|
44d96ad100 | ||
|
|
77184c7b52 | ||
|
|
9b43f2f03a | ||
|
|
8ee011c7f5 | ||
|
|
0509fe10be | ||
|
|
6f0a2c0288 | ||
|
|
e2ed618056 | ||
|
|
dc91e55cec | ||
|
|
47919bd434 | ||
|
|
c7fd1999db | ||
|
|
0335086df8 | ||
|
|
0cc3026b07 | ||
|
|
8802cfa32a | ||
|
|
ce06809970 | ||
|
|
252de16c13 | ||
|
|
43952fdc31 |
2
.github/actions/vulkan/action.yml
vendored
2
.github/actions/vulkan/action.yml
vendored
@@ -3,7 +3,7 @@ description: Downloads and installs Vulkan SDK.
|
||||
inputs:
|
||||
vulkan-version:
|
||||
description: 'Vulkan SDK release version (e.g. 1.2.198.1).'
|
||||
default: '1.2.198.1'
|
||||
default: '1.3.290.0'
|
||||
required: false
|
||||
runs:
|
||||
using: "composite"
|
||||
|
||||
87
.github/data/Build Settings.json
vendored
Normal file
87
.github/data/Build Settings.json
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"ID": "2364031e4e327637c1ad88b415fa756e",
|
||||
"TypeName": "FlaxEditor.Content.Settings.BuildSettings",
|
||||
"EngineBuild": 6605,
|
||||
"Data": {
|
||||
"OutputName": "${PROJECT_NAME}",
|
||||
"MaxAssetsPerPackage": 4096,
|
||||
"MaxPackageSizeMB": 1024,
|
||||
"ContentKey": 0,
|
||||
"ForDistribution": false,
|
||||
"SkipPackaging": true,
|
||||
"AdditionalAssets": null,
|
||||
"AdditionalScenes": null,
|
||||
"AdditionalAssetFolders": null,
|
||||
"ShadersNoOptimize": false,
|
||||
"ShadersGenerateDebugData": false,
|
||||
"SkipDefaultFonts": false,
|
||||
"SkipDotnetPackaging": false,
|
||||
"SkipUnusedDotnetLibsPackaging": true,
|
||||
"Presets": [
|
||||
{
|
||||
"Name": "Development",
|
||||
"Targets": [
|
||||
{
|
||||
"Name": "Windows",
|
||||
"Output": "Output\\Windows",
|
||||
"Platform": 2,
|
||||
"Mode": 1,
|
||||
"CustomDefines": null,
|
||||
"PreBuildAction": null,
|
||||
"PostBuildAction": null
|
||||
},
|
||||
{
|
||||
"Name": "Linux",
|
||||
"Output": "Output\\Linux",
|
||||
"Platform": 6,
|
||||
"Mode": 1,
|
||||
"CustomDefines": null,
|
||||
"PreBuildAction": null,
|
||||
"PostBuildAction": null
|
||||
},
|
||||
{
|
||||
"Name": "Mac",
|
||||
"Output": "Output\\Mac",
|
||||
"Platform": 13,
|
||||
"Mode": 1,
|
||||
"CustomDefines": null,
|
||||
"PreBuildAction": null,
|
||||
"PostBuildAction": null
|
||||
},
|
||||
{
|
||||
"Name": "Android",
|
||||
"Output": "Output\\Android",
|
||||
"Platform": 9,
|
||||
"Mode": 1,
|
||||
"CustomDefines": null,
|
||||
"PreBuildAction": null,
|
||||
"PostBuildAction": null
|
||||
},
|
||||
{
|
||||
"Name": "iOS",
|
||||
"Output": "Output\\iOS",
|
||||
"Platform": 14,
|
||||
"Mode": 1,
|
||||
"CustomDefines": null,
|
||||
"PreBuildAction": null,
|
||||
"PostBuildAction": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Release",
|
||||
"Targets": [
|
||||
{
|
||||
"Name": "Windows",
|
||||
"Output": "Output\\Windows",
|
||||
"Platform": 2,
|
||||
"Mode": 2,
|
||||
"CustomDefines": null,
|
||||
"PreBuildAction": null,
|
||||
"PostBuildAction": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
5
.github/data/Cook.ps1
vendored
Normal file
5
.github/data/Cook.ps1
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
Write-Output "Cooking Game"
|
||||
Start-Process -filepath "Binaries\Editor\Win64\Development\FlaxEditor.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows"'
|
||||
|
||||
Write-Output "Testing Game"
|
||||
Start-Process -filepath "FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null'
|
||||
11
.github/data/ExitOnEsc.cs
vendored
Normal file
11
.github/data/ExitOnEsc.cs
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
using FlaxEngine;
|
||||
|
||||
public class ExitOnEsc : Script
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void OnUpdate()
|
||||
{
|
||||
// Exit as soon as game starts update loaded level
|
||||
Engine.RequestExit();
|
||||
}
|
||||
}
|
||||
2
.github/workflows/build_ios.yml
vendored
2
.github/workflows/build_ios.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
# Game
|
||||
game-windows:
|
||||
name: Game (iOS, Release ARM64)
|
||||
runs-on: "macos-latest"
|
||||
runs-on: "macos-14"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
4
.github/workflows/build_linux.yml
vendored
4
.github/workflows/build_linux.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
4
.github/workflows/build_mac.yml
vendored
4
.github/workflows/build_mac.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
# Editor
|
||||
editor-mac:
|
||||
name: Editor (Mac, Development ARM64)
|
||||
runs-on: "macos-latest"
|
||||
runs-on: "macos-14"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
# Game
|
||||
game-mac:
|
||||
name: Game (Mac, Release ARM64)
|
||||
runs-on: "macos-latest"
|
||||
runs-on: "macos-14"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
4
.github/workflows/cd.yml
vendored
4
.github/workflows/cd.yml
vendored
@@ -140,7 +140,7 @@ jobs:
|
||||
# Mac
|
||||
package-mac-editor:
|
||||
name: Editor (Mac)
|
||||
runs-on: "macos-latest"
|
||||
runs-on: "macos-14"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
path: Output/FlaxEditorMac.zip
|
||||
package-mac-game:
|
||||
name: Game (Mac)
|
||||
runs-on: "macos-latest"
|
||||
runs-on: "macos-14"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
48
.github/workflows/cooking.yml
vendored
Normal file
48
.github/workflows/cooking.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Cooker
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: false
|
||||
|
||||
jobs:
|
||||
# Cook on Mac
|
||||
cook-mac:
|
||||
name: Cook (Mac)
|
||||
runs-on: "macos-14"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
- name: Setup .NET Workload
|
||||
run: |
|
||||
dotnet workload install ios
|
||||
- name: Print .NET info
|
||||
run: |
|
||||
dotnet --info
|
||||
dotnet workload --info
|
||||
- name: Checkout LFS
|
||||
run: |
|
||||
git lfs version
|
||||
git lfs pull
|
||||
- name: Get Flax Samples
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
repository: FlaxEngine/FlaxSamples
|
||||
path: FlaxSamples
|
||||
- name: Patch Files
|
||||
run: |
|
||||
cp .github/data/ExitOnEsc.cs FlaxSamples/MaterialsFeaturesTour/Source/Game
|
||||
cp ".github/data/Build Settings.json" "FlaxSamples/MaterialsFeaturesTour/Content/Settings"
|
||||
- name: Build Editor
|
||||
run: |
|
||||
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor
|
||||
- name: Cook Game (iOS)
|
||||
run: |
|
||||
./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS"
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Build
|
||||
run: |
|
||||
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Redirect to our own Git LFS server
|
||||
[lfs]
|
||||
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
#url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
locksverify = false
|
||||
|
||||
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialAxisFocus.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialAxisFocus.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialAxisLocked.flax
(Stored with Git LFS)
Normal file
BIN
Content/Editor/Gizmo/MaterialAxisLocked.flax
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialAxisX.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialAxisX.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialAxisY.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialAxisY.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialAxisZ.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialAxisZ.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialSphere.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialSphere.flax
(Stored with Git LFS)
Binary file not shown.
19
Content/Editor/Scripting/ActorTemplate.cpp
Normal file
19
Content/Editor/Scripting/ActorTemplate.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
%copyright%#include "%filename%.h"
|
||||
|
||||
%class%::%class%(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void %class%::OnEnable()
|
||||
{
|
||||
Actor::OnEnable();
|
||||
// Here you can add code that needs to be called when script is enabled (eg. register for events)
|
||||
}
|
||||
|
||||
void %class%::OnDisable()
|
||||
{
|
||||
Actor::OnDisable();
|
||||
// Here you can add code that needs to be called when script is disabled (eg. unregister from events)
|
||||
}
|
||||
39
Content/Editor/Scripting/ActorTemplate.cs
Normal file
39
Content/Editor/Scripting/ActorTemplate.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
%copyright%using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace %namespace%;
|
||||
|
||||
/// <summary>
|
||||
/// %class% Actor.
|
||||
/// </summary>
|
||||
public class %class% : Actor
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void OnBeginPlay()
|
||||
{
|
||||
base.OnBeginPlay();
|
||||
// Here you can add code that needs to be called when Actor added to the game. This is called during edit time as well.
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEndPlay()
|
||||
{
|
||||
base.OnEndPlay();
|
||||
// Here you can add code that needs to be called when Actor removed to the game. This is called during edit time as well.
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
// Here you can add code that needs to be called when Actor is enabled (eg. register for events). This is called during edit time as well.
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
// Here you can add code that needs to be called when Actor is disabled (eg. unregister from events). This is called during edit time as well.
|
||||
}
|
||||
}
|
||||
13
Content/Editor/Scripting/ActorTemplate.h
Normal file
13
Content/Editor/Scripting/ActorTemplate.h
Normal file
@@ -0,0 +1,13 @@
|
||||
%copyright%#pragma once
|
||||
|
||||
#include "Engine/Level/Actor.h"
|
||||
|
||||
API_CLASS() class %module%%class% : public Actor
|
||||
{
|
||||
API_AUTO_SERIALIZATION();
|
||||
DECLARE_SCENE_OBJECT(%class%);
|
||||
|
||||
// [Actor]
|
||||
void OnEnable() override;
|
||||
void OnDisable() override;
|
||||
};
|
||||
13
Content/Editor/Scripting/EmptyClassTemplate.cs
Normal file
13
Content/Editor/Scripting/EmptyClassTemplate.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
%copyright%using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace %namespace%;
|
||||
|
||||
/// <summary>
|
||||
/// %class% class.
|
||||
/// </summary>
|
||||
public class %class%
|
||||
{
|
||||
|
||||
}
|
||||
13
Content/Editor/Scripting/EmptyInterfaceTemplate.cs
Normal file
13
Content/Editor/Scripting/EmptyInterfaceTemplate.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
%copyright%using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace %namespace%;
|
||||
|
||||
/// <summary>
|
||||
/// %class% interface.
|
||||
/// </summary>
|
||||
public interface %class%
|
||||
{
|
||||
|
||||
}
|
||||
13
Content/Editor/Scripting/EmptyStructTemplate.cs
Normal file
13
Content/Editor/Scripting/EmptyStructTemplate.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
%copyright%using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace %namespace%;
|
||||
|
||||
/// <summary>
|
||||
/// %class% struct.
|
||||
/// </summary>
|
||||
public struct %class%
|
||||
{
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 9,
|
||||
"Revision": 0,
|
||||
"Build": 6605
|
||||
"Build": 6606
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
|
||||
@@ -13,6 +13,7 @@
|
||||
"Configuration": {
|
||||
"UseCSharp": true,
|
||||
"UseLargeWorlds": false,
|
||||
"UseDotNet": true
|
||||
"UseDotNet": true,
|
||||
"UseSDL": true
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
@@ -112,6 +112,12 @@ namespace FlaxEditor.Content
|
||||
throw new TargetException("Missing Visual Script asset.");
|
||||
_type.Asset.SetScriptInstanceParameterValue(_parameter.Name, (Object)obj, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Invoke(object obj, object[] parameters)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
sealed class VisualScriptMethodInfo : IScriptMemberInfo
|
||||
@@ -240,6 +246,14 @@ namespace FlaxEditor.Content
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Invoke(object obj, object[] parameters)
|
||||
{
|
||||
if (!_type.Asset)
|
||||
throw new TargetException("Missing Visual Script asset.");
|
||||
return _type.Asset.InvokeMethod(_index, obj, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context proxy object for C# script files.
|
||||
/// Context proxy object for C# Script files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
|
||||
[ContentContextMenu("New/C#/C# Script")]
|
||||
@@ -89,6 +89,23 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context proxy object for C# Actor files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
|
||||
[ContentContextMenu("New/C#/C# Actor")]
|
||||
public class CSharpActorProxy : CSharpProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "C# Actor";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void GetTemplatePath(out string path)
|
||||
{
|
||||
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.cs");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context proxy object for empty C# files.
|
||||
/// </summary>
|
||||
@@ -105,4 +122,55 @@ namespace FlaxEditor.Content
|
||||
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/CSharpEmptyTemplate.cs");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context proxy object for empty C# class files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
|
||||
[ContentContextMenu("New/C#/C# Class")]
|
||||
public class CSharpEmptyClassProxy : CSharpProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "C# Class";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void GetTemplatePath(out string path)
|
||||
{
|
||||
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyClassTemplate.cs");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context proxy object for empty C# struct files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
|
||||
[ContentContextMenu("New/C#/C# Struct")]
|
||||
public class CSharpEmptyStructProxy : CSharpProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "C# Struct";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void GetTemplatePath(out string path)
|
||||
{
|
||||
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyStructTemplate.cs");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context proxy object for empty C# interface files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
|
||||
[ContentContextMenu("New/C#/C# Interface")]
|
||||
public class CSharpEmptyInterfaceProxy : CSharpProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "C# Interface";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void GetTemplatePath(out string path)
|
||||
{
|
||||
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyInterfaceTemplate.cs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,24 @@ namespace FlaxEditor.Content
|
||||
sourceTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.cpp");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context proxy object for C++ Actor files.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.CppProxy" />
|
||||
[ContentContextMenu("New/C++/C++ Actor")]
|
||||
public class CppActorProxy : CppProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "C++ Actor";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void GetTemplatePaths(out string headerTemplate, out string sourceTemplate)
|
||||
{
|
||||
headerTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.h");
|
||||
sourceTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.cpp");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context proxy object for C++ Json Asset files.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
@@ -127,12 +128,41 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
_isSetBlocked = true;
|
||||
Initialize(layout);
|
||||
ShowButtons();
|
||||
Refresh();
|
||||
_isSetBlocked = false;
|
||||
|
||||
CurrentCustomEditor = prev;
|
||||
}
|
||||
|
||||
private void ShowButtons()
|
||||
{
|
||||
var values = Values;
|
||||
if (values == null || values.HasDifferentTypes)
|
||||
return;
|
||||
var type = TypeUtils.GetObjectType(values[0]);
|
||||
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (!method.HasAttribute(typeof(ButtonAttribute)) ||
|
||||
method.ParametersCount != 0)
|
||||
continue;
|
||||
var attribute = method.GetAttribute<ButtonAttribute>();
|
||||
var text = string.IsNullOrEmpty(attribute.Text) ? Utilities.Utils.GetPropertyNameUI(method.Name) : attribute.Text;
|
||||
var tooltip = string.IsNullOrEmpty(attribute.Tooltip) ? Editor.Instance.CodeDocs.GetTooltip(method) : attribute.Tooltip;
|
||||
var button = _layout.Button(text, tooltip);
|
||||
button.Button.Tag = method;
|
||||
button.Button.ButtonClicked += OnButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnButtonClicked(Button button)
|
||||
{
|
||||
var method = (ScriptMemberInfo)button.Tag;
|
||||
var obj = method.IsStatic ? null : Values[0];
|
||||
method.Invoke(obj);
|
||||
}
|
||||
|
||||
internal static CustomEditor CurrentCustomEditor;
|
||||
|
||||
internal void OnChildCreated(CustomEditor child)
|
||||
@@ -271,8 +301,16 @@ namespace FlaxEditor.CustomEditors
|
||||
_valueToSet = null;
|
||||
|
||||
// Assign value
|
||||
for (int i = 0; i < _values.Count; i++)
|
||||
_values[i] = val;
|
||||
if (val is IList l && l.Count == _values.Count && _values.Count > 1)
|
||||
{
|
||||
for (int i = 0; i < _values.Count; i++)
|
||||
_values[i] = l[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < _values.Count; i++)
|
||||
_values[i] = val;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -11,7 +12,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
class BezierCurveObjectEditor<T> : CustomEditor where T : struct
|
||||
{
|
||||
private bool _isSetting;
|
||||
private int _firstTimeShow;
|
||||
private BezierCurveEditor<T> _curve;
|
||||
private Splitter _splitter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -20,6 +23,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_curve = item.CustomControl;
|
||||
_curve.Height = 120.0f;
|
||||
_curve.Edited += OnCurveEdited;
|
||||
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
|
||||
_splitter = new Splitter
|
||||
{
|
||||
Moved = OnSplitterMoved,
|
||||
Parent = _curve,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
|
||||
Bounds = new Rectangle(0, _curve.Height - Splitter.DefaultHeight, _curve.Width, Splitter.DefaultHeight),
|
||||
};
|
||||
}
|
||||
|
||||
private void OnCurveEdited()
|
||||
@@ -32,6 +43,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_isSetting = false;
|
||||
}
|
||||
|
||||
private void OnSplitterMoved(Float2 location)
|
||||
{
|
||||
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
@@ -44,12 +60,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_curve.SetKeyframes(value.Keyframes);
|
||||
_isSetting = false;
|
||||
}
|
||||
if (_firstTimeShow-- > 0)
|
||||
_curve.ShowWholeCurve();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_curve = null;
|
||||
_splitter = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
@@ -111,7 +130,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
class LinearCurveObjectEditor<T> : CustomEditor where T : struct
|
||||
{
|
||||
private bool _isSetting;
|
||||
private int _firstTimeShow;
|
||||
private LinearCurveEditor<T> _curve;
|
||||
private Splitter _splitter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -120,6 +141,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_curve = item.CustomControl;
|
||||
_curve.Height = 120.0f;
|
||||
_curve.Edited += OnCurveEdited;
|
||||
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
|
||||
_splitter = new Splitter
|
||||
{
|
||||
Moved = OnSplitterMoved,
|
||||
Parent = _curve,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
|
||||
Bounds = new Rectangle(0, _curve.Height - Splitter.DefaultHeight, _curve.Width, Splitter.DefaultHeight),
|
||||
};
|
||||
}
|
||||
|
||||
private void OnCurveEdited()
|
||||
@@ -132,6 +161,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_isSetting = false;
|
||||
}
|
||||
|
||||
private void OnSplitterMoved(Float2 location)
|
||||
{
|
||||
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
@@ -144,12 +178,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_curve.SetKeyframes(value.Keyframes);
|
||||
_isSetting = false;
|
||||
}
|
||||
if (_firstTimeShow-- > 0)
|
||||
_curve.ShowWholeCurve();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_curve = null;
|
||||
_splitter = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
|
||||
@@ -424,6 +424,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
presenter.Undo.AddAction(multiAction);
|
||||
presenter.Control.Focus();
|
||||
|
||||
// Scroll to bottom of script control where a new script is added.
|
||||
if (presenter.Panel.Parent is Panel p && Editor.Instance.Options.Options.Interface.ScrollToScriptOnAdd)
|
||||
{
|
||||
var loc = ScriptsEditor.Layout.Control.BottomLeft;
|
||||
p.ScrollViewTo(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,20 +407,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.Editors.GenericEditor" />
|
||||
public class UIControlControlEditor : GenericEditor
|
||||
{
|
||||
private Type _cachedType;
|
||||
private ScriptType[] _valueTypes;
|
||||
private bool _anchorDropDownClosed = true;
|
||||
private Button _pivotRelativeButton;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_cachedType = null;
|
||||
if (HasDifferentTypes)
|
||||
{
|
||||
// TODO: support stable editing multiple different control types (via generic way or for transform-only)
|
||||
return;
|
||||
}
|
||||
|
||||
// Set control type button
|
||||
var space = layout.Space(20);
|
||||
var buttonText = "Set Type";
|
||||
@@ -445,12 +438,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
|
||||
// Add control type helper label
|
||||
if (!Values.HasDifferentTypes)
|
||||
{
|
||||
var type = Values[0].GetType();
|
||||
_cachedType = type;
|
||||
var label = layout.AddPropertyItem("Type", "The type of the created control.");
|
||||
label.Label(type.FullName);
|
||||
label.Label(Values[0].GetType().FullName);
|
||||
}
|
||||
_valueTypes = Values.ValuesTypes;
|
||||
|
||||
// Show control properties
|
||||
base.Initialize(layout);
|
||||
@@ -720,17 +713,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
if (_cachedType != null)
|
||||
// Automatic layout rebuild if control type gets changed
|
||||
if (_valueTypes != null &&
|
||||
!Values.HasNull &&
|
||||
!Utils.ArraysEqual(_valueTypes, Values.ValuesTypes))
|
||||
{
|
||||
// Automatic layout rebuild if control type gets changed
|
||||
var type = Values.HasNull ? null : Values[0].GetType();
|
||||
if (type != _cachedType)
|
||||
{
|
||||
RebuildLayout();
|
||||
return;
|
||||
}
|
||||
RebuildLayout();
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh anchors
|
||||
// Refresh anchors
|
||||
if (Values != null && Values[0] != null)
|
||||
{
|
||||
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
|
||||
if (xEq != _cachedXEq || yEq != _cachedYEq)
|
||||
{
|
||||
|
||||
@@ -292,5 +292,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
if (_validator != null)
|
||||
{
|
||||
_validator.OnDestroy();
|
||||
_validator = null;
|
||||
}
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,9 +898,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_pickerValidator.OnDestroy();
|
||||
_pickerValidator = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private bool ValidateActors(ActorNode node)
|
||||
|
||||
@@ -7,6 +7,7 @@ using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -23,6 +24,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public class FlaxObjectRefPickerControl : Control
|
||||
{
|
||||
private ScriptType _type;
|
||||
private ActorTreeNode _linkedTreeNode;
|
||||
private Object _value;
|
||||
private string _valueName;
|
||||
private bool _supportsPickDropDown;
|
||||
@@ -300,7 +302,43 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
// Picker dropdown menu
|
||||
if (_supportsPickDropDown && (isSelected ? button2Rect : button1Rect).Contains(ref location))
|
||||
{
|
||||
ShowDropDownMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
|
||||
// Highlight actor or script reference
|
||||
if (!_hasValidDragOver && !IsDragOver)
|
||||
{
|
||||
Actor actor = _value as Actor;
|
||||
if (actor == null && _value is Script script)
|
||||
actor = script.Actor;
|
||||
if (actor != null)
|
||||
{
|
||||
if (_linkedTreeNode != null && _linkedTreeNode.Actor == actor)
|
||||
{
|
||||
_linkedTreeNode.ExpandAllParents();
|
||||
_linkedTreeNode.StartHighlight();
|
||||
}
|
||||
else
|
||||
{
|
||||
_linkedTreeNode = Editor.Instance.Scene.GetActorNode(actor).TreeNode;
|
||||
_linkedTreeNode.ExpandAllParents();
|
||||
Editor.Instance.Windows.SceneWin.SceneTreePanel.ScrollViewTo(_linkedTreeNode, true);
|
||||
_linkedTreeNode.StartHighlight();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset valid drag over if still true at this point
|
||||
if (_hasValidDragOver)
|
||||
_hasValidDragOver = false;
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
@@ -326,6 +364,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Check if has object selected
|
||||
if (_value != null)
|
||||
{
|
||||
if (_linkedTreeNode != null)
|
||||
{
|
||||
_linkedTreeNode.StopHighlight();
|
||||
_linkedTreeNode = null;
|
||||
}
|
||||
|
||||
// Select object
|
||||
if (_value is Actor actor)
|
||||
Editor.Instance.SceneEditing.Select(actor);
|
||||
@@ -469,6 +513,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_value = null;
|
||||
_type = ScriptType.Null;
|
||||
_valueName = null;
|
||||
_linkedTreeNode = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use the average for sliding different values.
|
||||
/// </summary>
|
||||
public virtual bool AllowSlidingForDifferentValues => true;
|
||||
|
||||
private ValueChanged _valueChanged;
|
||||
private float _defaultSlidingSpeed;
|
||||
private bool _slidingEnded = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -46,18 +55,31 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
XElement = grid.FloatValue();
|
||||
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
ZElement = grid.FloatValue();
|
||||
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
if (LinkedLabel != null)
|
||||
{
|
||||
@@ -69,33 +91,118 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
|
||||
private void OnXValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.X;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnYValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.Y;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnZValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.Z;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var useCachedAngles = isSliding && token == _cachedToken;
|
||||
|
||||
float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.ValueBox.Value;
|
||||
float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.ValueBox.Value;
|
||||
float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.ValueBox.Value;
|
||||
|
||||
x = Mathf.UnwindDegrees(x);
|
||||
y = Mathf.UnwindDegrees(y);
|
||||
z = Mathf.UnwindDegrees(z);
|
||||
|
||||
if (!useCachedAngles)
|
||||
|
||||
if (HasDifferentValues && Values.Count > 1)
|
||||
{
|
||||
_cachedAngles = new Float3(x, y, z);
|
||||
var xValue = XElement.ValueBox.Value;
|
||||
var yValue = YElement.ValueBox.Value;
|
||||
var zValue = ZElement.ValueBox.Value;
|
||||
|
||||
xValue = Mathf.UnwindDegrees(xValue);
|
||||
yValue = Mathf.UnwindDegrees(yValue);
|
||||
zValue = Mathf.UnwindDegrees(zValue);
|
||||
|
||||
var value = new Float3(xValue, yValue, zValue);
|
||||
|
||||
_cachedToken = token;
|
||||
|
||||
var newObjects = new object[Values.Count];
|
||||
// Handle Sliding
|
||||
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||
{
|
||||
Float3 average = Float3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = (Quaternion)Values[0];
|
||||
var euler = v.EulerAngles;
|
||||
|
||||
average += euler;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
var newValue = value - average;
|
||||
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var val = (Quaternion)v;
|
||||
Quaternion.Euler(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0, out Quaternion qVal);
|
||||
v = val * qVal;
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
|
||||
// Capture last sliding value
|
||||
if (_slidingEnded)
|
||||
_slidingEnded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
object v = Values[i];
|
||||
var val = (Quaternion)v;
|
||||
var euler = val.EulerAngles;
|
||||
Quaternion.Euler(_valueChanged == ValueChanged.X ? xValue : euler.X, _valueChanged == ValueChanged.Y ? yValue : euler.Y, _valueChanged == ValueChanged.Z ? zValue : euler.Z, out Quaternion qVal);
|
||||
v = val * qVal;
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newObjects, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.ValueBox.Value;
|
||||
float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.ValueBox.Value;
|
||||
float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.ValueBox.Value;
|
||||
|
||||
_cachedToken = token;
|
||||
x = Mathf.UnwindDegrees(x);
|
||||
y = Mathf.UnwindDegrees(y);
|
||||
z = Mathf.UnwindDegrees(z);
|
||||
|
||||
Quaternion.Euler(x, y, z, out Quaternion value);
|
||||
SetValue(value, token);
|
||||
if (!useCachedAngles)
|
||||
{
|
||||
_cachedAngles = new Float3(x, y, z);
|
||||
}
|
||||
|
||||
_cachedToken = token;
|
||||
|
||||
Quaternion.Euler(x, y, z, out Quaternion value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -112,7 +219,73 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
// Get which values are different
|
||||
bool xDifferent = false;
|
||||
bool yDifferent = false;
|
||||
bool zDifferent = false;
|
||||
Float3 cachedFirstValue = Float3.Zero;
|
||||
Float3 average = Float3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var value = (Quaternion)Values[i];
|
||||
var euler = value.EulerAngles;
|
||||
|
||||
average += euler;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
cachedFirstValue = euler;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Mathf.NearEqual(cachedFirstValue.X, value.X))
|
||||
xDifferent = true;
|
||||
if (!Mathf.NearEqual(cachedFirstValue.Y, value.Y))
|
||||
yDifferent = true;
|
||||
if (!Mathf.NearEqual(cachedFirstValue.Z, value.Z))
|
||||
zDifferent = true;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
if (!xDifferent)
|
||||
{
|
||||
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
XElement.ValueBox.Value = average.X;
|
||||
else
|
||||
XElement.ValueBox.SlideSpeed = 0;
|
||||
XElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!yDifferent)
|
||||
{
|
||||
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
YElement.ValueBox.Value = average.Y;
|
||||
else
|
||||
YElement.ValueBox.SlideSpeed = 0;
|
||||
YElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!zDifferent)
|
||||
{
|
||||
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
ZElement.ValueBox.Value = average.Z;
|
||||
else
|
||||
ZElement.ValueBox.SlideSpeed = 0;
|
||||
ZElement.ValueBox.Text = "---";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -121,6 +294,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement.ValueBox.Value = euler.X;
|
||||
YElement.ValueBox.Value = euler.Y;
|
||||
ZElement.ValueBox.Value = euler.Z;
|
||||
|
||||
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,27 @@ using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// The value changed by custom Vector3 editors.
|
||||
/// </summary>
|
||||
public enum ValueChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// X value changed.
|
||||
/// </summary>
|
||||
X = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Y value changed.
|
||||
/// </summary>
|
||||
Y = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Z value changed.
|
||||
/// </summary>
|
||||
Z = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Vector3 value type properties.
|
||||
/// </summary>
|
||||
@@ -49,14 +70,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// </summary>
|
||||
public bool LinkValues = false;
|
||||
|
||||
private enum ValueChanged
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Z = 2
|
||||
}
|
||||
/// <summary>
|
||||
/// Whether to use the average for sliding different values.
|
||||
/// </summary>
|
||||
public virtual bool AllowSlidingForDifferentValues => true;
|
||||
|
||||
private ValueChanged _valueChanged;
|
||||
private float _defaultSlidingSpeed;
|
||||
private bool _slidingEnded = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -83,19 +104,32 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement.SetLimits(limit);
|
||||
XElement.SetCategory(category);
|
||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||
XElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.SetCategory(category);
|
||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||
YElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
ZElement = grid.FloatValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.SetCategory(category);
|
||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||
ZElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
if (LinkedLabel != null)
|
||||
{
|
||||
@@ -118,8 +152,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.X;
|
||||
_valueChanged = ValueChanged.X;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
@@ -127,8 +160,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.Y;
|
||||
_valueChanged = ValueChanged.Y;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
@@ -136,8 +168,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.Z;
|
||||
_valueChanged = ValueChanged.Z;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
@@ -191,14 +222,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Float3(xValue, yValue, zValue);
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
else if (v is Float3)
|
||||
v = (Float3)value;
|
||||
else if (v is Double3)
|
||||
v = (Double3)value;
|
||||
SetValue(v, token);
|
||||
if (HasDifferentValues && Values.Count > 1)
|
||||
{
|
||||
var newObjects = new object[Values.Count];
|
||||
// Handle Sliding
|
||||
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||
{
|
||||
// TODO: handle linked values
|
||||
Float3 average = Float3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var castedValue = Float3.Zero;
|
||||
if (v is Vector3 asVector3)
|
||||
castedValue = asVector3;
|
||||
else if (v is Float3 asFloat3)
|
||||
castedValue = asFloat3;
|
||||
else if (v is Double3 asDouble3)
|
||||
castedValue = asDouble3;
|
||||
|
||||
average += castedValue;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
var newValue = value - average;
|
||||
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
if (v is Vector3 asVector3)
|
||||
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
else if (v is Float3 asFloat3)
|
||||
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
else if (v is Double3 asDouble3)
|
||||
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
|
||||
// Capture last sliding value
|
||||
if (_slidingEnded)
|
||||
_slidingEnded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle linked values
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
object v = Values[i];
|
||||
if (v is Vector3 asVector3)
|
||||
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
|
||||
else if (v is Float3 asFloat3)
|
||||
v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z);
|
||||
else if (v is Double3 asDouble3)
|
||||
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newObjects, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
else if (v is Float3)
|
||||
v = (Float3)value;
|
||||
else if (v is Double3)
|
||||
v = (Double3)value;
|
||||
SetValue(v, token);
|
||||
}
|
||||
}
|
||||
|
||||
private float GetRatio(float value, float initialValue)
|
||||
@@ -218,7 +314,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
// Get which values are different
|
||||
bool xDifferent = false;
|
||||
bool yDifferent = false;
|
||||
bool zDifferent = false;
|
||||
Float3 cachedFirstValue = Float3.Zero;
|
||||
Float3 average = Float3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var value = Float3.Zero;
|
||||
if (v is Vector3 asVector3)
|
||||
value = asVector3;
|
||||
else if (v is Float3 asFloat3)
|
||||
value = asFloat3;
|
||||
else if (v is Double3 asDouble3)
|
||||
value = asDouble3;
|
||||
|
||||
average += value;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
cachedFirstValue = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Mathf.NearEqual(cachedFirstValue.X, value.X))
|
||||
xDifferent = true;
|
||||
if (!Mathf.NearEqual(cachedFirstValue.Y, value.Y))
|
||||
yDifferent = true;
|
||||
if (!Mathf.NearEqual(cachedFirstValue.Z, value.Z))
|
||||
zDifferent = true;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
if (!xDifferent)
|
||||
{
|
||||
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
XElement.ValueBox.Value = average.X;
|
||||
else
|
||||
XElement.ValueBox.SlideSpeed = 0;
|
||||
XElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!yDifferent)
|
||||
{
|
||||
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
YElement.ValueBox.Value = average.Y;
|
||||
else
|
||||
YElement.ValueBox.SlideSpeed = 0;
|
||||
YElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!zDifferent)
|
||||
{
|
||||
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
ZElement.ValueBox.Value = average.Z;
|
||||
else
|
||||
ZElement.ValueBox.SlideSpeed = 0;
|
||||
ZElement.ValueBox.Text = "---";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -232,6 +400,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement.ValueBox.Value = value.X;
|
||||
YElement.ValueBox.Value = value.Y;
|
||||
ZElement.ValueBox.Value = value.Z;
|
||||
|
||||
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,6 +434,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use the average for sliding different values.
|
||||
/// </summary>
|
||||
public virtual bool AllowSlidingForDifferentValues => true;
|
||||
|
||||
private ValueChanged _valueChanged;
|
||||
private float _defaultSlidingSpeed;
|
||||
private bool _slidingEnded = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -284,20 +468,33 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement = grid.DoubleValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.SetCategory(category);
|
||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||
|
||||
YElement = grid.DoubleValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.SetCategory(category);
|
||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
ZElement = grid.DoubleValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.SetCategory(category);
|
||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
if (LinkedLabel != null)
|
||||
{
|
||||
@@ -316,22 +513,115 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
}
|
||||
|
||||
private void OnXValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.X;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnYValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.Y;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnZValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.Z;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var xValue = XElement.ValueBox.Value;
|
||||
var yValue = YElement.ValueBox.Value;
|
||||
var zValue = ZElement.ValueBox.Value;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Double3(XElement.ValueBox.Value, YElement.ValueBox.Value, ZElement.ValueBox.Value);
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
else if (v is Float3)
|
||||
v = (Float3)value;
|
||||
else if (v is Double3)
|
||||
v = (Double3)value;
|
||||
SetValue(v, token);
|
||||
var value = new Double3(xValue, yValue, zValue);
|
||||
if (HasDifferentValues && Values.Count > 1)
|
||||
{
|
||||
var newObjects = new object[Values.Count];
|
||||
// Handle Sliding
|
||||
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||
{
|
||||
// TODO: handle linked values
|
||||
Double3 average = Double3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var castedValue = Double3.Zero;
|
||||
if (v is Vector3 asVector3)
|
||||
castedValue = asVector3;
|
||||
else if (v is Float3 asFloat3)
|
||||
castedValue = asFloat3;
|
||||
else if (v is Double3 asDouble3)
|
||||
castedValue = asDouble3;
|
||||
|
||||
average += castedValue;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
var newValue = value - average;
|
||||
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
if (v is Vector3 asVector3)
|
||||
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
else if (v is Float3 asFloat3)
|
||||
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? (float)newValue.X : 0, _valueChanged == ValueChanged.Y ? (float)newValue.Y : 0, _valueChanged == ValueChanged.Z ? (float)newValue.Z : 0);
|
||||
else if (v is Double3 asDouble3)
|
||||
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
|
||||
// Capture last sliding value
|
||||
if (_slidingEnded)
|
||||
_slidingEnded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle linked values
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
object v = Values[i];
|
||||
if (v is Vector3 asVector3)
|
||||
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
|
||||
else if (v is Float3 asFloat3)
|
||||
v = new Float3(_valueChanged == ValueChanged.X ? (float)xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? (float)yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? (float)zValue : asFloat3.Z);
|
||||
else if (v is Double3 asDouble3)
|
||||
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newObjects, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
else if (v is Float3)
|
||||
v = (Float3)value;
|
||||
else if (v is Double3)
|
||||
v = (Double3)value;
|
||||
SetValue(v, token);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -341,7 +631,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
// Get which values are different
|
||||
bool xDifferent = false;
|
||||
bool yDifferent = false;
|
||||
bool zDifferent = false;
|
||||
Double3 cachedFirstValue = Double3.Zero;
|
||||
Double3 average = Double3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var value = Double3.Zero;
|
||||
if (v is Vector3 asVector3)
|
||||
value = asVector3;
|
||||
else if (v is Float3 asFloat3)
|
||||
value = asFloat3;
|
||||
else if (v is Double3 asDouble3)
|
||||
value = asDouble3;
|
||||
|
||||
average += value;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
cachedFirstValue = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Mathd.NearEqual(cachedFirstValue.X, value.X))
|
||||
xDifferent = true;
|
||||
if (!Mathd.NearEqual(cachedFirstValue.Y, value.Y))
|
||||
yDifferent = true;
|
||||
if (!Mathd.NearEqual(cachedFirstValue.Z, value.Z))
|
||||
zDifferent = true;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
if (!xDifferent)
|
||||
{
|
||||
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
XElement.ValueBox.Value = average.X;
|
||||
else
|
||||
XElement.ValueBox.SlideSpeed = 0;
|
||||
XElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!yDifferent)
|
||||
{
|
||||
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
YElement.ValueBox.Value = average.Y;
|
||||
else
|
||||
YElement.ValueBox.SlideSpeed = 0;
|
||||
YElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!zDifferent)
|
||||
{
|
||||
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
ZElement.ValueBox.Value = average.Z;
|
||||
else
|
||||
ZElement.ValueBox.SlideSpeed = 0;
|
||||
ZElement.ValueBox.Text = "---";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -355,6 +717,19 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement.ValueBox.Value = value.X;
|
||||
YElement.ValueBox.Value = value.Y;
|
||||
ZElement.ValueBox.Value = value.Z;
|
||||
|
||||
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
{
|
||||
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
{
|
||||
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
{
|
||||
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -385,10 +386,21 @@ namespace FlaxEditor.CustomEditors
|
||||
if (instanceValues.Count != Count)
|
||||
throw new ArgumentException();
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
if (value is IList l && l.Count == Count && Count > 1)
|
||||
{
|
||||
Info.SetValue(instanceValues[i], value);
|
||||
this[i] = Info.GetValue(instanceValues[i]);
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
Info.SetValue(instanceValues[i], l[i]);
|
||||
this[i] = Info.GetValue(instanceValues[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
Info.SetValue(instanceValues[i], value);
|
||||
this[i] = Info.GetValue(instanceValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -673,6 +673,7 @@ bool Editor::Init()
|
||||
Managed = New<ManagedEditor>();
|
||||
|
||||
// Show splash screen
|
||||
if (!CommandLine::Options.Headless.IsTrue())
|
||||
{
|
||||
PROFILE_CPU_NAMED("Splash");
|
||||
if (EditorImpl::Splash == nullptr)
|
||||
|
||||
@@ -49,9 +49,9 @@ namespace FlaxEditor
|
||||
}
|
||||
|
||||
private readonly List<EditorModule> _modules = new List<EditorModule>(16);
|
||||
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode;
|
||||
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode, _autoExit;
|
||||
private string _projectToOpen;
|
||||
private float _lastAutoSaveTimer;
|
||||
private float _lastAutoSaveTimer, _autoExitTimeout = 0.1f;
|
||||
private Button _saveNowButton;
|
||||
private Button _cancelSaveButton;
|
||||
private bool _autoSaveNow;
|
||||
@@ -258,10 +258,11 @@ namespace FlaxEditor
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
internal void Init(bool isHeadless, bool skipCompile, bool newProject, Guid startupScene)
|
||||
internal void Init(StartupFlags flags, Guid startupScene)
|
||||
{
|
||||
Log("Setting up C# Editor...");
|
||||
_isHeadlessMode = isHeadless;
|
||||
_isHeadlessMode = flags.HasFlag(StartupFlags.Headless);
|
||||
_autoExit = flags.HasFlag(StartupFlags.Exit);
|
||||
_startupSceneCmdLine = startupScene;
|
||||
|
||||
Profiler.BeginEvent("Projects");
|
||||
@@ -297,11 +298,11 @@ namespace FlaxEditor
|
||||
StateMachine = new EditorStateMachine(this);
|
||||
Undo = new EditorUndo(this);
|
||||
|
||||
if (newProject)
|
||||
if (flags.HasFlag(StartupFlags.NewProject))
|
||||
InitProject();
|
||||
EnsureState<LoadingState>();
|
||||
Log("Editor init");
|
||||
if (isHeadless)
|
||||
if (_isHeadlessMode)
|
||||
Log("Running in headless mode");
|
||||
|
||||
// Note: we don't sort modules before Init (optimized)
|
||||
@@ -357,7 +358,7 @@ namespace FlaxEditor
|
||||
InitializationStart?.Invoke();
|
||||
|
||||
// Start Editor initialization ending phrase (will wait for scripts compilation result)
|
||||
StateMachine.LoadingState.StartInitEnding(skipCompile);
|
||||
StateMachine.LoadingState.StartInitEnding(flags.HasFlag(StartupFlags.SkipCompile));
|
||||
}
|
||||
|
||||
internal void RegisterModule(EditorModule module)
|
||||
@@ -486,6 +487,15 @@ namespace FlaxEditor
|
||||
{
|
||||
StateMachine.Update();
|
||||
UpdateAutoSave();
|
||||
if (_autoExit && StateMachine.CurrentState == StateMachine.EditingSceneState)
|
||||
{
|
||||
_autoExitTimeout -= Time.UnscaledGameTime;
|
||||
if (_autoExitTimeout < 0.0f)
|
||||
{
|
||||
Log("Auto exit");
|
||||
Engine.RequestExit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!StateMachine.IsPlayMode)
|
||||
{
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPicker"/> class.
|
||||
/// </summary>
|
||||
/// <param name="assetType">The assets types that this picker accepts.</param>
|
||||
/// <param name="assetType">The asset types that this picker accepts.</param>
|
||||
/// <param name="location">The control location.</param>
|
||||
public AssetPicker(ScriptType assetType, Float2 location)
|
||||
: base(location, new Float2(DefaultIconSize + ButtonsOffset + ButtonsSize, DefaultIconSize))
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
#define USE_IS_FOREGROUND
|
||||
#else
|
||||
#endif
|
||||
#if PLATFORM_SDL
|
||||
#define USE_SDL_WORKAROUNDS
|
||||
#endif
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
@@ -111,7 +114,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the empty menu popup o na screen.
|
||||
/// Shows the empty menu popup on a screen.
|
||||
/// </summary>
|
||||
/// <param name="control">The target control.</param>
|
||||
/// <param name="area">The target control area to cover.</param>
|
||||
@@ -215,21 +218,26 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
desc.AllowMaximize = false;
|
||||
desc.AllowDragAndDrop = false;
|
||||
desc.IsTopmost = true;
|
||||
desc.IsRegularWindow = false;
|
||||
desc.Type = WindowType.Popup;
|
||||
desc.Parent = parentWin.Window;
|
||||
desc.HasSizingFrame = false;
|
||||
OnWindowCreating(ref desc);
|
||||
_window = Platform.CreateWindow(ref desc);
|
||||
_window.GotFocus += OnWindowGotFocus;
|
||||
_window.LostFocus += OnWindowLostFocus;
|
||||
|
||||
#if USE_IS_FOREGROUND && USE_SDL_WORKAROUNDS
|
||||
// The focus between popup and parent windows doesn't change, force hide the popup when clicked on parent
|
||||
parentWin.Window.MouseDown += OnWindowMouseDown;
|
||||
_window.Closed += () => parentWin.Window.MouseDown -= OnWindowMouseDown;
|
||||
#endif
|
||||
|
||||
// Attach to the window
|
||||
_parentCM = parent as ContextMenuBase;
|
||||
Parent = _window.GUI;
|
||||
|
||||
// Show
|
||||
Visible = true;
|
||||
if (_window == null)
|
||||
return;
|
||||
_window.Show();
|
||||
PerformLayout();
|
||||
_previouslyFocused = parentWin.FocusedControl;
|
||||
@@ -378,6 +386,17 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnWindowMouseDown(ref Float2 mousePosition, MouseButton button, ref bool handled)
|
||||
{
|
||||
// The user clicked outside the popup window
|
||||
Hide();
|
||||
}
|
||||
#else
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
var child = _childCM;
|
||||
@@ -391,6 +410,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
@@ -489,7 +509,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Let root context menu to check if none of the popup windows
|
||||
if (_parentCM == null && !IsForeground)
|
||||
{
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
if (!IsMouseOver)
|
||||
Hide();
|
||||
#else
|
||||
Hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
@@ -29,6 +28,7 @@ namespace FlaxEditor.GUI
|
||||
internal Float2 _mousePos = Float2.Minimum;
|
||||
internal bool _isMovingSelection;
|
||||
internal bool _isMovingTangent;
|
||||
internal bool _movedView;
|
||||
internal bool _movedKeyframes;
|
||||
private TangentPoint _movingTangent;
|
||||
private Float2 _movingSelectionStart;
|
||||
@@ -190,31 +190,28 @@ namespace FlaxEditor.GUI
|
||||
// Moving view
|
||||
if (_rightMouseDown)
|
||||
{
|
||||
var delta = location - _movingViewLastPos;
|
||||
var movingViewPos = Parent.PointToParent(PointToParent(location));
|
||||
var delta = movingViewPos - _movingViewLastPos;
|
||||
if (_editor.CustomViewPanning != null)
|
||||
delta = _editor.CustomViewPanning(delta);
|
||||
delta *= GetUseModeMask(_editor.EnablePanning) * _editor.ViewScale;
|
||||
delta *= GetUseModeMask(_editor.EnablePanning);
|
||||
if (delta.LengthSquared > 0.01f)
|
||||
{
|
||||
_editor._mainPanel.ViewOffset += delta;
|
||||
_movingViewLastPos = location;
|
||||
_movingViewLastPos = movingViewPos;
|
||||
_movedView = true;
|
||||
if (_editor.CustomViewPanning != null)
|
||||
{
|
||||
Cursor = CursorType.SizeAll;
|
||||
if (Cursor == CursorType.Default)
|
||||
Cursor = CursorType.SizeAll;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (_editor.EnablePanning)
|
||||
{
|
||||
case UseMode.Vertical:
|
||||
Cursor = CursorType.SizeNS;
|
||||
break;
|
||||
case UseMode.Horizontal:
|
||||
Cursor = CursorType.SizeWE;
|
||||
break;
|
||||
case UseMode.On:
|
||||
Cursor = CursorType.SizeAll;
|
||||
break;
|
||||
case UseMode.Vertical: Cursor = CursorType.SizeNS; break;
|
||||
case UseMode.Horizontal: Cursor = CursorType.SizeWE; break;
|
||||
case UseMode.On: Cursor = CursorType.SizeAll; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,11 +231,10 @@ namespace FlaxEditor.GUI
|
||||
else if (_isMovingTangent)
|
||||
{
|
||||
var viewRect = _editor._mainPanel.GetClientArea();
|
||||
var direction = _movingTangent.IsIn ? -1.0f : 1.0f;
|
||||
var k = _editor.GetKeyframe(_movingTangent.Index);
|
||||
var kv = _editor.GetKeyframeValue(k);
|
||||
var value = _editor.Accessor.GetCurveValue(ref kv, _movingTangent.Component);
|
||||
_movingTangent.TangentValue = direction * (PointToKeyframes(location, ref viewRect).Y - value);
|
||||
_movingTangent.TangentValue = PointToKeyframes(location, ref viewRect).Y - value;
|
||||
_editor.UpdateTangents();
|
||||
Cursor = CursorType.SizeNS;
|
||||
_movedKeyframes = true;
|
||||
@@ -299,7 +295,8 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
_rightMouseDown = true;
|
||||
_rightMouseDownPos = location;
|
||||
_movingViewLastPos = location;
|
||||
_movedView = false;
|
||||
_movingViewLastPos = Parent.PointToParent(PointToParent(location));
|
||||
}
|
||||
|
||||
// Check if any node is under the mouse
|
||||
@@ -444,7 +441,7 @@ namespace FlaxEditor.GUI
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
// Check if no move has been made at all
|
||||
if (Float2.Distance(ref location, ref _rightMouseDownPos) < 2.0f)
|
||||
if (!_movedView)
|
||||
{
|
||||
var selectionCount = _editor.SelectionCount;
|
||||
var point = GetChildAt(location) as KeyframePoint;
|
||||
@@ -512,6 +509,27 @@ namespace FlaxEditor.GUI
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
// Add keyframe on double click
|
||||
var child = GetChildAt(location);
|
||||
if (child is not KeyframePoint &&
|
||||
child is not TangentPoint &&
|
||||
_editor.KeyframesCount < _editor.MaxKeyframes)
|
||||
{
|
||||
var viewRect = _editor._mainPanel.GetClientArea();
|
||||
var pos = PointToKeyframes(location, ref viewRect);
|
||||
_editor.AddKeyframe(pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseWheel(Float2 location, float delta)
|
||||
{
|
||||
@@ -519,10 +537,29 @@ namespace FlaxEditor.GUI
|
||||
return true;
|
||||
|
||||
// Zoom in/out
|
||||
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && RootWindow.GetKey(KeyboardKeys.Control))
|
||||
var zoom = RootWindow.GetKey(KeyboardKeys.Control);
|
||||
var zoomAlt = RootWindow.GetKey(KeyboardKeys.Shift);
|
||||
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && (zoom || zoomAlt))
|
||||
{
|
||||
// TODO: preserve the view center point for easier zooming
|
||||
_editor.ViewScale += GetUseModeMask(_editor.EnableZoom) * (delta * 0.1f);
|
||||
// Cache mouse location in curve-space
|
||||
var viewRect = _editor._mainPanel.GetClientArea();
|
||||
var locationInKeyframes = PointToKeyframes(location, ref viewRect);
|
||||
var locationInEditorBefore = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
|
||||
|
||||
// Scale relative to the curve size
|
||||
var scale = new Float2(delta * 0.1f);
|
||||
_editor._mainPanel.GetDesireClientArea(out var mainPanelArea);
|
||||
var curveScale = mainPanelArea.Size / _editor._contents.Size;
|
||||
scale *= curveScale;
|
||||
if (zoomAlt)
|
||||
scale.X = 0; // Scale Y axis only
|
||||
scale *= GetUseModeMask(_editor.EnableZoom); // Mask scale depending on allowed usage
|
||||
_editor.ViewScale += scale;
|
||||
|
||||
// Zoom towards the mouse position
|
||||
var locationInEditorAfter = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
|
||||
var locationInEditorDelta = locationInEditorAfter - locationInEditorBefore;
|
||||
_editor.ViewOffset -= locationInEditorDelta;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -671,8 +671,22 @@ namespace FlaxEditor.GUI
|
||||
/// <inheritdoc />
|
||||
public override void ShowWholeCurve()
|
||||
{
|
||||
ViewScale = ApplyUseModeMask(EnableZoom, _mainPanel.Size / _contents.Size, ViewScale);
|
||||
ViewOffset = ApplyUseModeMask(EnablePanning, -_mainPanel.ControlsBounds.Location, ViewOffset);
|
||||
_mainPanel.GetDesireClientArea(out var mainPanelArea);
|
||||
ViewScale = ApplyUseModeMask(EnableZoom, mainPanelArea.Size / _contents.Size, ViewScale);
|
||||
Float2 minPos = Float2.Maximum;
|
||||
foreach (var point in _points)
|
||||
{
|
||||
var pos = point.PointToParent(point.Location);
|
||||
Float2.Min(ref minPos, ref pos, out minPos);
|
||||
}
|
||||
var minPosPoint = _contents.PointToParent(ref minPos);
|
||||
var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0);
|
||||
scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll);
|
||||
if (_mainPanel.HScrollBar != null)
|
||||
_mainPanel.HScrollBar.TargetValue = scroll.X;
|
||||
if (_mainPanel.VScrollBar != null)
|
||||
_mainPanel.VScrollBar.TargetValue = scroll.Y;
|
||||
|
||||
UpdateKeyframes();
|
||||
}
|
||||
|
||||
@@ -923,6 +937,11 @@ namespace FlaxEditor.GUI
|
||||
KeyframesEditorUtils.Paste(this);
|
||||
return true;
|
||||
}
|
||||
else if (options.FocusSelection.Process(this))
|
||||
{
|
||||
ShowWholeCurve();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1384,9 +1403,7 @@ namespace FlaxEditor.GUI
|
||||
// Calculate bounds
|
||||
var bounds = _points[0].Bounds;
|
||||
for (var i = 1; i < _points.Count; i++)
|
||||
{
|
||||
bounds = Rectangle.Union(bounds, _points[i].Bounds);
|
||||
}
|
||||
|
||||
// Adjust contents bounds to fill the curve area
|
||||
if (EnablePanning != UseMode.Off || !ShowCollapsed)
|
||||
@@ -1632,6 +1649,7 @@ namespace FlaxEditor.GUI
|
||||
var o = _keyframes[p.Index - 1];
|
||||
var oValue = Accessor.GetCurveValue(ref o.Value, p.Component);
|
||||
var slope = (value - oValue) / (k.Time - o.Time);
|
||||
slope = -slope;
|
||||
Accessor.SetCurveValue(slope, ref k.TangentIn, p.Component);
|
||||
}
|
||||
|
||||
@@ -2116,9 +2134,7 @@ namespace FlaxEditor.GUI
|
||||
// Calculate bounds
|
||||
var bounds = _points[0].Bounds;
|
||||
for (int i = 1; i < _points.Count; i++)
|
||||
{
|
||||
bounds = Rectangle.Union(bounds, _points[i].Bounds);
|
||||
}
|
||||
|
||||
// Adjust contents bounds to fill the curve area
|
||||
if (EnablePanning != UseMode.Off || !ShowCollapsed)
|
||||
@@ -2184,12 +2200,12 @@ namespace FlaxEditor.GUI
|
||||
|
||||
var tangent = t.TangentValue;
|
||||
var direction = t.IsIn ? -1.0f : 1.0f;
|
||||
var offset = 30.0f * direction;
|
||||
var offset = 30.0f;
|
||||
var location = GetKeyframePoint(ref k, selectedComponent);
|
||||
t.Size = KeyframesSize / ViewScale;
|
||||
t.Location = new Float2
|
||||
(
|
||||
location.X * UnitsPerSecond - t.Width * 0.5f + offset,
|
||||
location.X * UnitsPerSecond - t.Width * 0.5f + offset * direction,
|
||||
location.Y * -UnitsPerSecond - t.Height * 0.5f + curveContentAreaBounds.Height - offset * tangent
|
||||
);
|
||||
|
||||
@@ -2265,14 +2281,13 @@ namespace FlaxEditor.GUI
|
||||
var startTangent = Accessor.GetCurveValue(ref startK.TangentOut, component);
|
||||
var endTangent = Accessor.GetCurveValue(ref endK.TangentIn, component);
|
||||
|
||||
var offset = (end.X - start.X) * 0.5f;
|
||||
|
||||
var tangentScale = (endK.Time - startK.Time) / 3.0f;
|
||||
var p1 = PointFromKeyframes(start, ref viewRect);
|
||||
var p2 = PointFromKeyframes(start + new Float2(offset, startTangent * offset), ref viewRect);
|
||||
var p3 = PointFromKeyframes(end - new Float2(offset, endTangent * offset), ref viewRect);
|
||||
var p2 = PointFromKeyframes(start + new Float2(0, startTangent * tangentScale), ref viewRect);
|
||||
var p3 = PointFromKeyframes(end + new Float2(0, endTangent * tangentScale), ref viewRect);
|
||||
var p4 = PointFromKeyframes(end, ref viewRect);
|
||||
|
||||
Render2D.DrawBezier(p1, p2, p3, p4, color);
|
||||
Render2D.DrawSpline(p1, p2, p3, p4, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
Proxy.Window.MouseUp += OnMouseUp;
|
||||
Proxy.Window.MouseMove += OnMouseMove;
|
||||
Proxy.Window.LostFocus += OnLostFocus;
|
||||
|
||||
// Start tracking mouse
|
||||
Proxy.Window.StartTrackingMouse(false);
|
||||
_toMove.Window.Window.MouseUp += OnMouseUp; // Intercept the drag release mouse event from source window
|
||||
|
||||
// Update window GUI
|
||||
Proxy.Window.GUI.PerformLayout();
|
||||
@@ -77,13 +75,16 @@ namespace FlaxEditor.GUI.Docking
|
||||
// Update rectangles
|
||||
UpdateRects();
|
||||
|
||||
// Hide base window
|
||||
window.Hide();
|
||||
|
||||
// Enable hit window presentation
|
||||
Proxy.Window.RenderingEnabled = true;
|
||||
Proxy.Window.Show();
|
||||
Proxy.Window.Focus();
|
||||
|
||||
// Hide base window
|
||||
window.Hide();
|
||||
|
||||
// Start tracking mouse
|
||||
Proxy.Window.StartTrackingMouse(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -101,6 +102,8 @@ namespace FlaxEditor.GUI.Docking
|
||||
Proxy.Window.MouseUp -= OnMouseUp;
|
||||
Proxy.Window.MouseMove -= OnMouseMove;
|
||||
Proxy.Window.LostFocus -= OnLostFocus;
|
||||
if (_toMove?.Window?.Window)
|
||||
_toMove.Window.Window.MouseUp -= OnMouseUp;
|
||||
|
||||
// Hide the proxy
|
||||
Proxy.Hide();
|
||||
@@ -438,7 +441,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.Type = WindowType.Utility;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
@@ -470,7 +473,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.Type = WindowType.Utility;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ActivateWhenFirstShown = false;
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.AllowMaximize = true;
|
||||
settings.AllowDragAndDrop = true;
|
||||
settings.IsTopmost = false;
|
||||
settings.IsRegularWindow = true;
|
||||
settings.Type = WindowType.Regular;
|
||||
settings.HasSizingFrame = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.ShowInTaskbar = true;
|
||||
|
||||
@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
@@ -292,6 +293,36 @@ namespace FlaxEditor.GUI.Input
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMoveRelative(Float2 mouseMotion)
|
||||
{
|
||||
var location = Root.TrackingMouseOffset;
|
||||
if (_isSliding)
|
||||
{
|
||||
// Update sliding
|
||||
ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update cursor type so user knows they can slide value
|
||||
if (CanUseSliding && SlideRect.Contains(location) && !_isSliding)
|
||||
{
|
||||
Cursor = CursorType.SizeWE;
|
||||
_cursorChanged = true;
|
||||
}
|
||||
else if (_cursorChanged && !_isSliding)
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
_cursorChanged = false;
|
||||
}
|
||||
|
||||
base.OnMouseMoveRelative(mouseMotion);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public sealed class MainMenu : ContainerControl
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
private bool _useCustomWindowSystem;
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
@@ -67,7 +67,7 @@ namespace FlaxEditor.GUI
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
@@ -166,7 +166,7 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
@@ -291,7 +291,7 @@ namespace FlaxEditor.GUI
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
|
||||
{
|
||||
@@ -321,7 +321,7 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
float x = 0;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Icon
|
||||
@@ -349,7 +349,7 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Buttons
|
||||
@@ -367,7 +367,7 @@ namespace FlaxEditor.GUI
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace FlaxEditor.GUI
|
||||
Text = text;
|
||||
|
||||
var style = Style.Current;
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
{
|
||||
BackgroundColorMouseOver = style.BackgroundHighlighted;
|
||||
|
||||
86
Source/Editor/GUI/Splitter.cs
Normal file
86
Source/Editor/GUI/Splitter.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI
|
||||
{
|
||||
sealed class Splitter : Control
|
||||
{
|
||||
private bool _clicked;
|
||||
|
||||
public Action<Float2> Moved;
|
||||
public const float DefaultHeight = 5.0f;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Style.Current;
|
||||
if (IsMouseOver || _clicked)
|
||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
|
||||
}
|
||||
|
||||
public override void OnEndMouseCapture()
|
||||
{
|
||||
base.OnEndMouseCapture();
|
||||
|
||||
_clicked = false;
|
||||
}
|
||||
|
||||
public override void Defocus()
|
||||
{
|
||||
base.Defocus();
|
||||
|
||||
_clicked = false;
|
||||
}
|
||||
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
base.OnMouseEnter(location);
|
||||
|
||||
Cursor = CursorType.SizeNS;
|
||||
}
|
||||
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
_clicked = true;
|
||||
Focus();
|
||||
StartMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
base.OnMouseMove(location);
|
||||
|
||||
if (_clicked)
|
||||
{
|
||||
Moved(location);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _clicked)
|
||||
{
|
||||
_clicked = false;
|
||||
EndMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
||||
continue;
|
||||
|
||||
// Draw all ticks
|
||||
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
|
||||
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 2);
|
||||
var lStep = _tickSteps[l];
|
||||
var lNextStep = _tickSteps[l + 1];
|
||||
int startTick = Mathf.FloorToInt(min / lStep);
|
||||
|
||||
@@ -19,87 +19,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// <seealso cref="MemberTrack" />
|
||||
public abstract class CurvePropertyTrackBase : MemberTrack, IKeyframesEditorContext
|
||||
{
|
||||
private sealed class Splitter : Control
|
||||
{
|
||||
private bool _clicked;
|
||||
internal CurvePropertyTrackBase _track;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Style.Current;
|
||||
if (IsMouseOver || _clicked)
|
||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
|
||||
}
|
||||
|
||||
public override void OnEndMouseCapture()
|
||||
{
|
||||
base.OnEndMouseCapture();
|
||||
|
||||
_clicked = false;
|
||||
}
|
||||
|
||||
public override void Defocus()
|
||||
{
|
||||
base.Defocus();
|
||||
|
||||
_clicked = false;
|
||||
}
|
||||
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
base.OnMouseEnter(location);
|
||||
|
||||
Cursor = CursorType.SizeNS;
|
||||
}
|
||||
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
_clicked = true;
|
||||
Focus();
|
||||
StartMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
base.OnMouseMove(location);
|
||||
|
||||
if (_clicked)
|
||||
{
|
||||
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
|
||||
if (!Mathf.NearEqual(height, _track._expandedHeight))
|
||||
{
|
||||
_track.Height = _track._expandedHeight = height;
|
||||
_track.Timeline.ArrangeTracks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _clicked)
|
||||
{
|
||||
_clicked = false;
|
||||
EndMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _curveEditingStartData;
|
||||
private float _expandedHeight = 120.0f;
|
||||
private Splitter _splitter;
|
||||
@@ -251,12 +170,21 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
{
|
||||
_splitter = new Splitter
|
||||
{
|
||||
_track = this,
|
||||
Moved = OnSplitterMoved,
|
||||
Parent = Curve,
|
||||
};
|
||||
}
|
||||
var splitterHeight = 5.0f;
|
||||
_splitter.Bounds = new Rectangle(0, Curve.Height - splitterHeight, Curve.Width, splitterHeight);
|
||||
_splitter.Bounds = new Rectangle(0, Curve.Height - Splitter.DefaultHeight, Curve.Width, Splitter.DefaultHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSplitterMoved(Float2 location)
|
||||
{
|
||||
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
|
||||
if (!Mathf.NearEqual(height, _expandedHeight))
|
||||
{
|
||||
Height = _expandedHeight = height;
|
||||
Timeline.ArrangeTracks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,19 @@ namespace FlaxEditor.GUI.Tree
|
||||
/// </summary>
|
||||
public const float DefaultNodeOffsetY = 0;
|
||||
|
||||
private const float _targetHighlightScale = 1.25f;
|
||||
private const float _highlightScaleAnimDuration = 0.85f;
|
||||
|
||||
private Tree _tree;
|
||||
|
||||
private bool _opened, _canChangeOrder;
|
||||
private float _animationProgress, _cachedHeight;
|
||||
private bool _isHightlighted;
|
||||
private float _targetHighlightTimeSec;
|
||||
private float _currentHighlightTimeSec;
|
||||
// Used to prevent showing highlight on double mouse click
|
||||
private float _debounceHighlightTime;
|
||||
private float _highlightScale;
|
||||
private bool _mouseOverArrow, _mouseOverHeader;
|
||||
private float _xOffset, _textWidth;
|
||||
private float _headerHeight = 16.0f;
|
||||
@@ -605,9 +614,47 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a box around the text to highlight the node.
|
||||
/// </summary>
|
||||
/// <param name="durationSec">The duration of the highlight in seconds.</param>
|
||||
public void StartHighlight(float durationSec = 3)
|
||||
{
|
||||
_isHightlighted = true;
|
||||
_targetHighlightTimeSec = durationSec;
|
||||
_currentHighlightTimeSec = 0;
|
||||
_debounceHighlightTime = 0;
|
||||
_highlightScale = 2f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops any current highlight.
|
||||
/// </summary>
|
||||
public void StopHighlight()
|
||||
{
|
||||
_isHightlighted = false;
|
||||
_targetHighlightTimeSec = 0;
|
||||
_currentHighlightTimeSec = 0;
|
||||
_debounceHighlightTime = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
// Highlight animations
|
||||
if (_isHightlighted)
|
||||
{
|
||||
_debounceHighlightTime += deltaTime;
|
||||
_currentHighlightTimeSec += deltaTime;
|
||||
|
||||
// In the first second, animate the highlight to shrink into it's resting position
|
||||
if (_currentHighlightTimeSec < _highlightScaleAnimDuration)
|
||||
_highlightScale = Mathf.Lerp(_highlightScale, _targetHighlightScale, _currentHighlightTimeSec);
|
||||
|
||||
if (_currentHighlightTimeSec >= _targetHighlightTimeSec)
|
||||
_isHightlighted = false;
|
||||
}
|
||||
|
||||
// Drop/down animation
|
||||
if (_animationProgress < 1.0f)
|
||||
{
|
||||
@@ -676,6 +723,18 @@ namespace FlaxEditor.GUI.Tree
|
||||
textRect.Width -= 18.0f;
|
||||
}
|
||||
|
||||
float textWidth = TextFont.GetFont().MeasureText(_text).X;
|
||||
Rectangle trueTextRect = textRect;
|
||||
trueTextRect.Width = textWidth;
|
||||
trueTextRect.Scale(_highlightScale);
|
||||
|
||||
if (_isHightlighted && _debounceHighlightTime > 0.1f)
|
||||
{
|
||||
Color highlightBackgroundColor = Editor.Instance.Options.Options.Visual.HighlightColor;
|
||||
highlightBackgroundColor = highlightBackgroundColor.AlphaMultiplied(0.3f);
|
||||
Render2D.FillRectangle(trueTextRect, highlightBackgroundColor);
|
||||
}
|
||||
|
||||
// Draw text
|
||||
Color textColor = CacheTextColor();
|
||||
Render2D.DrawText(TextFont.GetFont(), _text, textRect, textColor, TextAlignment.Near, TextAlignment.Center);
|
||||
@@ -730,6 +789,12 @@ namespace FlaxEditor.GUI.Tree
|
||||
}
|
||||
}
|
||||
|
||||
if (_isHightlighted && _debounceHighlightTime > 0.1f)
|
||||
{
|
||||
// Draw highlights
|
||||
Render2D.DrawRectangle(trueTextRect, Editor.Instance.Options.Options.Visual.HighlightColor, 3);
|
||||
}
|
||||
|
||||
// Base
|
||||
if (_opened)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEngine;
|
||||
using System;
|
||||
|
||||
namespace FlaxEditor.Gizmo
|
||||
{
|
||||
@@ -18,8 +21,13 @@ namespace FlaxEditor.Gizmo
|
||||
private MaterialInstance _materialAxisY;
|
||||
private MaterialInstance _materialAxisZ;
|
||||
private MaterialInstance _materialAxisFocus;
|
||||
private MaterialInstance _materialAxisLocked;
|
||||
private MaterialBase _materialSphere;
|
||||
|
||||
// Material Parameter Names
|
||||
const String _brightnessParamName = "Brightness";
|
||||
const String _opacityParamName = "Opacity";
|
||||
|
||||
private void InitDrawing()
|
||||
{
|
||||
// Axis Models
|
||||
@@ -34,6 +42,7 @@ namespace FlaxEditor.Gizmo
|
||||
_materialAxisY = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisY");
|
||||
_materialAxisZ = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisZ");
|
||||
_materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisFocus");
|
||||
_materialAxisLocked = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisLocked");
|
||||
_materialSphere = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialSphere");
|
||||
|
||||
// Ensure that every asset was loaded
|
||||
@@ -50,6 +59,25 @@ namespace FlaxEditor.Gizmo
|
||||
{
|
||||
Platform.Fatal("Failed to load transform gizmo resources.");
|
||||
}
|
||||
|
||||
// Setup editor options
|
||||
OnEditorOptionsChanged(Editor.Instance.Options.Options);
|
||||
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
float brightness = options.Visual.TransformGizmoBrightness;
|
||||
_materialAxisX.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisY.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisZ.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisLocked.SetParameterValue(_brightnessParamName, brightness);
|
||||
|
||||
float opacity = options.Visual.TransformGizmoOpacity;
|
||||
_materialAxisX.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisY.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisZ.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisLocked.SetParameterValue(_opacityParamName, opacity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -60,6 +88,21 @@ namespace FlaxEditor.Gizmo
|
||||
if (!_modelCube || !_modelCube.IsLoaded)
|
||||
return;
|
||||
|
||||
// Find out if any of the selected objects can not be moved
|
||||
bool gizmoLocked = false;
|
||||
if (Editor.Instance.StateMachine.IsPlayMode)
|
||||
{
|
||||
for (int i = 0; i < SelectionCount; i++)
|
||||
{
|
||||
var obj = GetSelectedObject(i);
|
||||
if (obj.CanTransform == false)
|
||||
{
|
||||
gizmoLocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As all axisMesh have the same pivot, add a little offset to the x axisMesh, this way SortDrawCalls is able to sort the draw order
|
||||
// https://github.com/FlaxEngine/FlaxEngine/issues/680
|
||||
|
||||
@@ -92,32 +135,38 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
transAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
|
||||
MaterialInstance xAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
transAxisMesh.Draw(ref renderContext, xAxisMaterialTransform, ref m3);
|
||||
|
||||
// Y axis
|
||||
Matrix.RotationX(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
transAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
|
||||
MaterialInstance yAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
transAxisMesh.Draw(ref renderContext, yAxisMaterialTransform, ref m3);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(Mathf.Pi, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
transAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
|
||||
MaterialInstance zAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
transAxisMesh.Draw(ref renderContext, zAxisMaterialTransform, ref m3);
|
||||
|
||||
// XY plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
|
||||
MaterialInstance xyPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
|
||||
cubeMesh.Draw(ref renderContext, xyPlaneMaterialTransform, ref m3);
|
||||
|
||||
// ZX plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
|
||||
MaterialInstance zxPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisY);
|
||||
cubeMesh.Draw(ref renderContext, zxPlaneMaterialTransform, ref m3);
|
||||
|
||||
// YZ plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
|
||||
MaterialInstance yzPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisZ);
|
||||
cubeMesh.Draw(ref renderContext, yzPlaneMaterialTransform, ref m3);
|
||||
|
||||
// Center sphere
|
||||
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
|
||||
@@ -136,15 +185,18 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
rotationAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
|
||||
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
rotationAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3);
|
||||
|
||||
// Y axis
|
||||
rotationAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1);
|
||||
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
rotationAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m1);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
rotationAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
|
||||
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
rotationAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3);
|
||||
|
||||
// Center box
|
||||
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
|
||||
@@ -163,32 +215,38 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref mx1, out m3);
|
||||
scaleAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
|
||||
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
scaleAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3);
|
||||
|
||||
// Y axis
|
||||
Matrix.RotationX(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
scaleAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
|
||||
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
scaleAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m3);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(Mathf.Pi, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
scaleAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
|
||||
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
scaleAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3);
|
||||
|
||||
// XY plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
|
||||
MaterialInstance xyPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
|
||||
cubeMesh.Draw(ref renderContext, xyPlaneMaterialScale, ref m3);
|
||||
|
||||
// ZX plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
|
||||
MaterialInstance zxPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ);
|
||||
cubeMesh.Draw(ref renderContext, zxPlaneMaterialScale, ref m3);
|
||||
|
||||
// YZ plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
|
||||
MaterialInstance yzPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY);
|
||||
cubeMesh.Draw(ref renderContext, yzPlaneMaterialScale, ref m3);
|
||||
|
||||
// Center box
|
||||
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
|
||||
|
||||
@@ -435,6 +435,9 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
case InputDevice::EventType::MouseMove:
|
||||
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
|
||||
break;
|
||||
case InputDevice::EventType::MouseMoveRelative:
|
||||
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
|
||||
break;
|
||||
case InputDevice::EventType::MouseLeave:
|
||||
window->OnMouseLeave();
|
||||
break;
|
||||
|
||||
@@ -176,7 +176,7 @@ ManagedEditor::~ManagedEditor()
|
||||
void ManagedEditor::Init()
|
||||
{
|
||||
// Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time
|
||||
void* args[4];
|
||||
void* args[2];
|
||||
MClass* mclass = GetClass();
|
||||
if (mclass == nullptr)
|
||||
{
|
||||
@@ -193,18 +193,22 @@ void ManagedEditor::Init()
|
||||
LOG(Fatal, "Failed to create editor instance.");
|
||||
}
|
||||
MObject* exception = nullptr;
|
||||
bool isHeadless = CommandLine::Options.Headless.IsTrue();
|
||||
bool skipCompile = CommandLine::Options.SkipCompile.IsTrue();
|
||||
bool newProject = CommandLine::Options.NewProject.IsTrue();
|
||||
args[0] = &isHeadless;
|
||||
args[1] = &skipCompile;
|
||||
args[2] = &newProject;
|
||||
StartupFlags flags = StartupFlags::None;
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
flags |= StartupFlags::Headless;
|
||||
if (CommandLine::Options.SkipCompile.IsTrue())
|
||||
flags |= StartupFlags::SkipCompile;
|
||||
if (CommandLine::Options.NewProject.IsTrue())
|
||||
flags |= StartupFlags::NewProject;
|
||||
if (CommandLine::Options.Exit.IsTrue())
|
||||
flags |= StartupFlags::Exit;
|
||||
args[0] = &flags;
|
||||
Guid sceneId;
|
||||
if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId)))
|
||||
{
|
||||
sceneId = Guid::Empty;
|
||||
}
|
||||
args[3] = &sceneId;
|
||||
args[1] = &sceneId;
|
||||
initMethod->Invoke(instance, args, &exception);
|
||||
if (exception)
|
||||
{
|
||||
@@ -219,7 +223,7 @@ void ManagedEditor::Init()
|
||||
WasExitCalled = false;
|
||||
|
||||
// Load scripts if auto-load on startup is disabled
|
||||
if (!ManagedEditorOptions.ForceScriptCompilationOnStartup || skipCompile)
|
||||
if (!ManagedEditorOptions.ForceScriptCompilationOnStartup || EnumHasAllFlags(flags, StartupFlags::SkipCompile))
|
||||
{
|
||||
LOG(Info, "Loading managed assemblies (due to disabled compilation on startup)");
|
||||
Scripting::Load();
|
||||
|
||||
@@ -22,6 +22,15 @@ API_CLASS(Namespace="FlaxEditor", Name="Editor", NoSpawn, NoConstructor) class M
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ManagedEditor);
|
||||
static Guid ObjectID;
|
||||
|
||||
API_ENUM(Attributes="Flags", Internal) enum class StartupFlags
|
||||
{
|
||||
None = 0,
|
||||
Headless = 1,
|
||||
SkipCompile = 2,
|
||||
NewProject = 4,
|
||||
Exit = 8,
|
||||
};
|
||||
|
||||
struct InternalOptions
|
||||
{
|
||||
byte AutoReloadScriptsOnMainWindowFocus = 1;
|
||||
@@ -258,3 +267,5 @@ public:
|
||||
// [ScriptingObject]
|
||||
void DestroyManaged() override;
|
||||
};
|
||||
|
||||
DECLARE_ENUM_OPERATORS(ManagedEditor::StartupFlags);
|
||||
|
||||
@@ -1136,9 +1136,14 @@ namespace FlaxEditor.Modules
|
||||
Proxy.Add(new SceneAnimationProxy());
|
||||
Proxy.Add(new CSharpScriptProxy());
|
||||
Proxy.Add(new CSharpEmptyProxy());
|
||||
Proxy.Add(new CSharpEmptyClassProxy());
|
||||
Proxy.Add(new CSharpEmptyStructProxy());
|
||||
Proxy.Add(new CSharpEmptyInterfaceProxy());
|
||||
Proxy.Add(new CSharpActorProxy());
|
||||
Proxy.Add(new CppAssetProxy());
|
||||
Proxy.Add(new CppStaticClassProxy());
|
||||
Proxy.Add(new CppScriptProxy());
|
||||
Proxy.Add(new CppActorProxy());
|
||||
Proxy.Add(new SceneProxy());
|
||||
Proxy.Add(new PrefabProxy());
|
||||
Proxy.Add(new IESProfileProxy());
|
||||
|
||||
@@ -274,11 +274,7 @@ namespace FlaxEditor.Modules
|
||||
private void Load()
|
||||
{
|
||||
if (!File.Exists(_cachePath))
|
||||
{
|
||||
Editor.LogWarning("Missing editor cache file.");
|
||||
return;
|
||||
}
|
||||
|
||||
_lastSaveTime = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
|
||||
@@ -99,6 +99,10 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
if (Editor.StateMachine.IsEditMode)
|
||||
{
|
||||
// Show Game window if hidden
|
||||
if (Editor.Windows.GameWin.IsHidden)
|
||||
Editor.Windows.GameWin.Show();
|
||||
|
||||
Editor.Log("[PlayMode] Start");
|
||||
|
||||
if (Editor.Options.Options.General.AutoReloadScriptsOnMainWindowFocus)
|
||||
@@ -131,6 +135,10 @@ namespace FlaxEditor.Modules
|
||||
if (!Editor.StateMachine.IsEditMode)
|
||||
return;
|
||||
|
||||
// Show Game window if hidden
|
||||
if (Editor.Windows.GameWin.IsHidden)
|
||||
Editor.Windows.GameWin.Show();
|
||||
|
||||
var firstScene = Content.Settings.GameSettings.Load().FirstScene;
|
||||
if (firstScene == Guid.Empty)
|
||||
{
|
||||
|
||||
@@ -381,7 +381,7 @@ namespace FlaxEditor.Modules
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
|
||||
// Add dummy control for drawing the main window borders if using a custom style
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
|
||||
#endif
|
||||
{
|
||||
|
||||
@@ -760,8 +760,10 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Skip OS sizing frame and implement it using LeftButtonHit
|
||||
settings.HasSizingFrame = false;
|
||||
#endif
|
||||
}
|
||||
#elif PLATFORM_LINUX
|
||||
settings.HasBorder = false;
|
||||
@@ -950,7 +952,10 @@ namespace FlaxEditor.Modules
|
||||
MainWindow = null;
|
||||
|
||||
// Capture project icon screenshot (not in play mode and if editor was used for some time)
|
||||
if (!Editor.StateMachine.IsPlayMode && Time.TimeSinceStartup >= 5.0f && GPUDevice.Instance?.RendererType != RendererType.Null)
|
||||
if (!Editor.StateMachine.IsPlayMode &&
|
||||
Time.TimeSinceStartup >= 5.0f &&
|
||||
!Editor.IsHeadlessMode &&
|
||||
GPUDevice.Instance?.RendererType != RendererType.Null)
|
||||
{
|
||||
Editor.Log("Capture project icon screenshot");
|
||||
_projectIconScreenshotTimeout = Time.TimeSinceStartup + 0.8f; // wait 800ms for a screenshot task
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")]
|
||||
public float InterfaceScale { get; set; } = 1.0f;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS || PLATFORM_SDL
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
|
||||
/// </summary>
|
||||
@@ -258,6 +258,13 @@ namespace FlaxEditor.Options
|
||||
|
||||
private TextAlignment _tooltipTextAlignment = TextAlignment.Center;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to scroll to the script when a script is added to an actor.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Interface"), EditorOrder(322)]
|
||||
public bool ScrollToScriptOnAdd { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamps prefix mode for output log messages.
|
||||
/// </summary>
|
||||
|
||||
@@ -22,37 +22,51 @@ namespace FlaxEditor.Options
|
||||
/// Gets or sets the first outline color.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Color), "0.039,0.827,0.156")]
|
||||
[EditorDisplay("Gizmo"), EditorOrder(100), Tooltip("The first color of the selection outline gradient.")]
|
||||
[EditorDisplay("Transform Gizmo"), EditorOrder(100), Tooltip("The first color of the selection outline gradient.")]
|
||||
public Color SelectionOutlineColor0 { get; set; } = new Color(0.039f, 0.827f, 0.156f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the second outline color.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Color), "0.019,0.615,0.101")]
|
||||
[EditorDisplay("Gizmo"), EditorOrder(101), Tooltip("The second color of the selection outline gradient.")]
|
||||
[EditorDisplay("Transform Gizmo"), EditorOrder(101), Tooltip("The second color of the selection outline gradient.")]
|
||||
public Color SelectionOutlineColor1 { get; set; } = new Color(0.019f, 0.615f, 0.101f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selection outline size for UI controls.
|
||||
/// </summary>
|
||||
[DefaultValue(2.0f)]
|
||||
[EditorDisplay("Gizmo", "UI Control Outline Size"), EditorOrder(100), Tooltip("The size of the selection outline for UI controls.")]
|
||||
[EditorDisplay("UI Gizmo", "UI Control Outline Size"), EditorOrder(103), Tooltip("The size of the selection outline for UI controls.")]
|
||||
public float UISelectionOutlineSize { get; set; } = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transform gizmo size.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f), Limit(0.01f, 100.0f, 0.01f)]
|
||||
[EditorDisplay("Gizmo"), EditorOrder(110), Tooltip("The transform gizmo size.")]
|
||||
[EditorDisplay("Transform Gizmo"), EditorOrder(110), Tooltip("The transform gizmo size.")]
|
||||
public float GizmoSize { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color used to highlight selected meshes and CSG surfaces.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Color), "0.0,0.533,1.0,1.0")]
|
||||
[EditorDisplay("Gizmo"), EditorOrder(200), Tooltip("The color used to highlight selected meshes and CSG surfaces.")]
|
||||
[EditorDisplay("Transform Gizmo"), EditorOrder(200), Tooltip("The color used to highlight selected meshes and CSG surfaces.")]
|
||||
public Color HighlightColor { get; set; } = new Color(0.0f, 0.533f, 1.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set a value indicating how bright the transform gizmo is. Value over 1 will result in the gizmo emitting light.
|
||||
/// </summary>
|
||||
[DefaultValue(1f), Range(0f, 5f)]
|
||||
[EditorDisplay("Transform Gizmo", "Gizmo Brightness"), EditorOrder(210)]
|
||||
public float TransformGizmoBrightness { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set a value indicating the opacity of the transform gizmo.
|
||||
/// </summary>
|
||||
[DefaultValue(1f), Range(0f, 1f)]
|
||||
[EditorDisplay("Transform Gizmo", "Gizmo Opacity"), EditorOrder(211)]
|
||||
public float TransformGizmoOpacity { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance.
|
||||
/// </summary>
|
||||
|
||||
@@ -293,6 +293,14 @@ namespace FlaxEditor.Scripting
|
||||
/// <param name="obj">The object whose member value will be modified.</param>
|
||||
/// <param name="value">The new member value.</param>
|
||||
void SetValue(object obj, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the method on a specific object (null if static) using the provided parameters.
|
||||
/// </summary>
|
||||
/// <param name="obj">The instance of the object to invoke its method. Use null for static methods.</param>
|
||||
/// <param name="parameters">List of parameters to provide.</param>
|
||||
/// <returns>The value returned by the method.</returns>
|
||||
object Invoke(object obj, object[] parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -691,6 +691,23 @@ namespace FlaxEditor.Scripting
|
||||
else
|
||||
_custom.SetValue(obj, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the method on a specific object (null if static) using the provided parameters.
|
||||
/// </summary>
|
||||
/// <param name="obj">The instance of the object to invoke its method. Use null for static methods.</param>
|
||||
/// <param name="parameters">List of parameters to provide.</param>
|
||||
/// <returns>The value returned by the method.</returns>
|
||||
public object Invoke(object obj = null, object[] parameters = null)
|
||||
{
|
||||
if (parameters == null)
|
||||
parameters = Array.Empty<object>();
|
||||
if (_managed is MethodInfo methodInfo)
|
||||
return methodInfo.Invoke(obj, parameters);
|
||||
if (_managed != null)
|
||||
throw new NotSupportedException();
|
||||
return _custom.Invoke(obj, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// Represents single blend point.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||
protected class BlendPoint : Control
|
||||
internal class BlendPoint : Control
|
||||
{
|
||||
private readonly BlendPointsEditor _editor;
|
||||
private readonly int _index;
|
||||
@@ -48,6 +48,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public int Index => _index;
|
||||
|
||||
/// <summary>
|
||||
/// Flag that indicates that user is moving this point with a mouse.
|
||||
/// </summary>
|
||||
public bool IsMouseDown => _isMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
||||
/// </summary>
|
||||
@@ -211,6 +216,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
|
||||
|
||||
/// <summary>
|
||||
/// BLend points array.
|
||||
/// </summary>
|
||||
internal IReadOnlyList<BlendPoint> BlendPoints => _blendPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
||||
/// </summary>
|
||||
@@ -374,6 +384,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <returns>The blend point control position.</returns>
|
||||
public Float2 BlendSpacePosToBlendPointPos(Float2 pos)
|
||||
{
|
||||
if (_rangeX.IsZero)
|
||||
{
|
||||
var data0 = (Float4)_node.Values[0];
|
||||
_rangeX = new Float2(data0.X, data0.Y);
|
||||
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
|
||||
}
|
||||
GetPointsArea(out var pointsArea);
|
||||
if (_is2D)
|
||||
{
|
||||
@@ -389,7 +405,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
pointsArea.Center.Y
|
||||
);
|
||||
}
|
||||
return pos - new Float2(BlendPoint.DefaultSize * 0.5f);
|
||||
return pos;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -424,7 +440,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
// Update blend point
|
||||
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location);
|
||||
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location) - BlendPoint.DefaultSize * 0.5f;
|
||||
var asset = Editor.Instance.ContentDatabase.FindAsset(animId);
|
||||
var tooltip = asset?.ShortName ?? string.Empty;
|
||||
tooltip += "\nX: " + location.X;
|
||||
@@ -532,81 +548,18 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
SetAsset((int)b.Tag, Guid.Empty);
|
||||
}
|
||||
|
||||
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||
{
|
||||
// Draw line
|
||||
Render2D.DrawLine(start, end, gridColor);
|
||||
|
||||
// Draw label
|
||||
var labelWidth = 50.0f;
|
||||
var labelHeight = 10.0f;
|
||||
var labelMargin = 2.0f;
|
||||
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
var hAlign = TextAlignment.Near;
|
||||
Rectangle labelRect;
|
||||
if (vertical)
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
return; // Don't overlap with the first horizontal label
|
||||
}
|
||||
else
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
{
|
||||
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||
hAlign = TextAlignment.Far;
|
||||
}
|
||||
}
|
||||
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Style.Current;
|
||||
var rect = new Rectangle(Float2.Zero, Size);
|
||||
var containsFocus = ContainsFocus;
|
||||
GetPointsArea(out var pointsArea);
|
||||
var data0 = (Float4)_node.Values[0];
|
||||
var rangeX = new Float2(data0.X, data0.Y);
|
||||
|
||||
// Background
|
||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||
//Render2D.DrawRectangle(pointsArea, Color.Red);
|
||||
_node.DrawEditorBackground(ref rect);
|
||||
|
||||
// Grid
|
||||
int splits = 10;
|
||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||
var labelColor = style.ForegroundDisabled;
|
||||
var labelFont = style.FontSmall;
|
||||
//var blendArea = BlendAreaRect;
|
||||
var blendArea = pointsArea;
|
||||
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float x = blendArea.Left + blendArea.Width * alpha;
|
||||
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
if (_is2D)
|
||||
{
|
||||
var rangeY = new Float2(data0.Z, data0.W);
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float y = blendArea.Top + blendArea.Height * alpha;
|
||||
float value = Mathf.Lerp(rangeY.X, rangeY.Y, alpha);
|
||||
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float y = blendArea.Center.Y;
|
||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||
}
|
||||
_node.DrawEditorGrid(ref rect);
|
||||
|
||||
base.Draw();
|
||||
|
||||
@@ -808,6 +761,87 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
|
||||
}
|
||||
|
||||
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||
{
|
||||
// Draw line
|
||||
Render2D.DrawLine(start, end, gridColor);
|
||||
|
||||
// Draw label
|
||||
var labelWidth = 50.0f;
|
||||
var labelHeight = 10.0f;
|
||||
var labelMargin = 2.0f;
|
||||
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
var hAlign = TextAlignment.Near;
|
||||
Rectangle labelRect;
|
||||
if (vertical)
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
return; // Don't overlap with the first horizontal label
|
||||
}
|
||||
else
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
{
|
||||
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||
hAlign = TextAlignment.Far;
|
||||
}
|
||||
}
|
||||
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom drawing logic for blend space background.
|
||||
/// </summary>
|
||||
public virtual void DrawEditorBackground(ref Rectangle rect)
|
||||
{
|
||||
var style = Style.Current;
|
||||
Render2D.FillRectangle(rect, style.Background.AlphaMultiplied(0.5f));
|
||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom drawing logic for blend space grid.
|
||||
/// </summary>
|
||||
public virtual void DrawEditorGrid(ref Rectangle rect)
|
||||
{
|
||||
var style = Style.Current;
|
||||
_editor.GetPointsArea(out var pointsArea);
|
||||
var data0 = (Float4)Values[0];
|
||||
var rangeX = new Float2(data0.X, data0.Y);
|
||||
int splits = 10;
|
||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||
var labelColor = style.ForegroundDisabled;
|
||||
var labelFont = style.FontSmall;
|
||||
//var blendArea = BlendAreaRect;
|
||||
var blendArea = pointsArea;
|
||||
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float x = blendArea.Left + blendArea.Width * alpha;
|
||||
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
if (_editor.Is2D)
|
||||
{
|
||||
var rangeY = new Float2(data0.Z, data0.W);
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float y = blendArea.Top + blendArea.Height * alpha;
|
||||
float value = Mathf.Lerp(rangeY.X, rangeY.Y, 1.0f - alpha);
|
||||
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float y = blendArea.Center.Y;
|
||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the editor UI.
|
||||
/// </summary>
|
||||
@@ -972,6 +1006,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
private readonly FloatValueBox _animationX;
|
||||
private readonly Label _animationYLabel;
|
||||
private readonly FloatValueBox _animationY;
|
||||
private Float2[] _triangles;
|
||||
private Color[] _triangleColors;
|
||||
private Float2[] _selectedTriangles;
|
||||
private Color[] _selectedColors;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
@@ -1040,6 +1078,143 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearTriangles()
|
||||
{
|
||||
// Remove cache
|
||||
_triangles = null;
|
||||
_triangleColors = null;
|
||||
_selectedTriangles = null;
|
||||
_selectedColors = null;
|
||||
}
|
||||
|
||||
private void CacheTriangles()
|
||||
{
|
||||
// Get locations of blend point vertices
|
||||
int pointsCount = _editor.PointsCount;
|
||||
int count = 0, j = 0;
|
||||
for (int i = 0; i < pointsCount; i++)
|
||||
{
|
||||
var animId = (Guid)Values[5 + i * 2];
|
||||
if (animId != Guid.Empty)
|
||||
count++;
|
||||
}
|
||||
var vertices = new Float2[count];
|
||||
for (int i = 0; i < pointsCount; i++)
|
||||
{
|
||||
var animId = (Guid)Values[5 + i * 2];
|
||||
if (animId != Guid.Empty)
|
||||
{
|
||||
var dataA = (Float4)Values[4 + i * 2];
|
||||
vertices[j++] = new Float2(dataA.X, dataA.Y);
|
||||
}
|
||||
}
|
||||
|
||||
// Triangulate
|
||||
_triangles = FlaxEngine.Utilities.Delaunay2D.Triangulate(vertices);
|
||||
_triangleColors = null;
|
||||
|
||||
// Fix incorrect triangles (mirror logic in AnimGraphBase::onNodeLoaded)
|
||||
if (_triangles == null || _triangles.Length == 0)
|
||||
{
|
||||
// Insert dummy triangles to have something working (eg. blend points are on the same axis)
|
||||
var triangles = new List<Float2>();
|
||||
int verticesLeft = vertices.Length;
|
||||
while (verticesLeft >= 3)
|
||||
{
|
||||
verticesLeft -= 3;
|
||||
triangles.Add(vertices[verticesLeft + 0]);
|
||||
triangles.Add(vertices[verticesLeft + 1]);
|
||||
triangles.Add(vertices[verticesLeft + 2]);
|
||||
}
|
||||
if (verticesLeft == 1)
|
||||
{
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[0]);
|
||||
}
|
||||
else if (verticesLeft == 2)
|
||||
{
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[1]);
|
||||
triangles.Add(vertices[0]);
|
||||
}
|
||||
_triangles = triangles.ToArray();
|
||||
}
|
||||
|
||||
// Project to the blend space for drawing
|
||||
for (int i = 0; i < _triangles.Length; i++)
|
||||
_triangles[i] = _editor.BlendSpacePosToBlendPointPos(_triangles[i]);
|
||||
|
||||
// Check if anything is selected
|
||||
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||
if (selectedIndex != -1)
|
||||
{
|
||||
// Find triangles that contain selected point
|
||||
var dataA = (Float4)Values[4 + selectedIndex * 2];
|
||||
var pos = _editor.BlendSpacePosToBlendPointPos(new Float2(dataA.X, dataA.Y));
|
||||
var selectedTriangles = new List<Float2>();
|
||||
var selectedColors = new List<Color>();
|
||||
var style = Style.Current;
|
||||
var triangleColor = style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f);
|
||||
var selectedTriangleColor = style.BackgroundSelected.AlphaMultiplied(0.6f);
|
||||
_triangleColors = new Color[_triangles.Length];
|
||||
for (int i = 0; i < _triangles.Length; i += 3)
|
||||
{
|
||||
var is0 = Float2.NearEqual(ref _triangles[i + 0], ref pos);
|
||||
var is1 = Float2.NearEqual(ref _triangles[i + 1], ref pos);
|
||||
var is2 = Float2.NearEqual(ref _triangles[i + 2], ref pos);
|
||||
if (is0 || is1 || is2)
|
||||
{
|
||||
selectedTriangles.Add(_triangles[i + 0]);
|
||||
selectedTriangles.Add(_triangles[i + 1]);
|
||||
selectedTriangles.Add(_triangles[i + 2]);
|
||||
selectedColors.Add(is0 ? Color.White : Color.Transparent);
|
||||
selectedColors.Add(is1 ? Color.White : Color.Transparent);
|
||||
selectedColors.Add(is2 ? Color.White : Color.Transparent);
|
||||
}
|
||||
_triangleColors[i + 0] = is0 ? selectedTriangleColor : triangleColor;
|
||||
_triangleColors[i + 1] = is1 ? selectedTriangleColor : triangleColor;
|
||||
_triangleColors[i + 2] = is2 ? selectedTriangleColor : triangleColor;
|
||||
}
|
||||
_selectedTriangles = selectedTriangles.ToArray();
|
||||
_selectedColors = selectedColors.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawEditorBackground(ref Rectangle rect)
|
||||
{
|
||||
base.DrawEditorBackground(ref rect);
|
||||
|
||||
// Draw triangulated multi blend space
|
||||
var style = Style.Current;
|
||||
if (_triangles == null)
|
||||
CacheTriangles();
|
||||
if (_triangleColors != null && (ContainsFocus || IsMouseOver))
|
||||
Render2D.FillTriangles(_triangles, _triangleColors);
|
||||
else
|
||||
Render2D.FillTriangles(_triangles, style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f));
|
||||
Render2D.DrawTriangles(_triangles, style.Foreground);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawEditorGrid(ref Rectangle rect)
|
||||
{
|
||||
base.DrawEditorGrid(ref rect);
|
||||
|
||||
// Highlight selected blend point
|
||||
var style = Style.Current;
|
||||
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||
if (selectedIndex != -1 && (ContainsFocus || IsMouseOver))
|
||||
{
|
||||
var point = _editor.BlendPoints[selectedIndex];
|
||||
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
|
||||
Render2D.PushTint(ref highlightColor);
|
||||
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
|
||||
Render2D.PopTint();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
||||
{
|
||||
@@ -1064,6 +1239,23 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_animationX.Enabled = isValid;
|
||||
_animationYLabel.Enabled = isValid;
|
||||
_animationY.Enabled = isValid;
|
||||
ClearTriangles();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnValuesChanged()
|
||||
{
|
||||
base.OnValuesChanged();
|
||||
|
||||
ClearTriangles();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLoaded(SurfaceNodeActions action)
|
||||
{
|
||||
base.OnLoaded(action);
|
||||
|
||||
ClearTriangles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1502,9 +1502,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
data = new object[]
|
||||
{
|
||||
filterText.Substring(2),
|
||||
new Color(1.0f, 1.0f, 1.0f, 0.2f),
|
||||
new Float2(400.0f, 400.0f),
|
||||
filterText.Substring(2), // Title
|
||||
new Color(1.0f, 1.0f, 1.0f, 0.2f), // Color
|
||||
new Float2(400.0f, 400.0f), // Size
|
||||
-1, // Order
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace FlaxEditor.Surface
|
||||
typeof(TooltipAttribute),
|
||||
typeof(HideInEditorAttribute),
|
||||
typeof(NoAnimateAttribute),
|
||||
typeof(ButtonAttribute),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -172,10 +172,22 @@ namespace FlaxEditor.Surface
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (_isRenaming && (!_renameTextBox.IsFocused || !RootWindow.IsFocused))
|
||||
if (_isRenaming)
|
||||
{
|
||||
Rename(_renameTextBox.Text);
|
||||
StopRenaming();
|
||||
// Stop renaming when clicking anywhere else
|
||||
if (!_renameTextBox.IsFocused || !RootWindow.IsFocused)
|
||||
{
|
||||
Rename(_renameTextBox.Text);
|
||||
StopRenaming();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rename on F2
|
||||
if (IsSelected && Editor.Instance.Options.Options.Input.Rename.Process(this))
|
||||
{
|
||||
StartRenaming();
|
||||
}
|
||||
}
|
||||
|
||||
base.Update(deltaTime);
|
||||
@@ -417,6 +429,7 @@ namespace FlaxEditor.Surface
|
||||
base.OnShowSecondaryContextMenu(menu, location);
|
||||
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Rename", StartRenaming);
|
||||
ContextMenuChildMenu cmOrder = menu.AddChildMenu("Order");
|
||||
{
|
||||
cmOrder.ContextMenu.AddButton("Bring Forward", () =>
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "ScreenUtilities.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
|
||||
Delegate<Color32> ScreenUtilities::PickColorDone;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#pragma comment(lib, "Gdi32.lib")
|
||||
|
||||
static HHOOK MouseCallbackHook;
|
||||
|
||||
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
|
||||
{
|
||||
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
|
||||
{
|
||||
UnhookWindowsHookEx(MouseCallbackHook);
|
||||
|
||||
// Push event with the picked color
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
return 1;
|
||||
}
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
HDC deviceContext = GetDC(NULL);
|
||||
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
|
||||
ReleaseDC(NULL, deviceContext);
|
||||
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
|
||||
if (MouseCallbackHook == NULL)
|
||||
{
|
||||
LOG(Warning, "Failed to set mouse hook.");
|
||||
LOG(Warning, "Error: {0}", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
#elif PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
X11::XColor color;
|
||||
|
||||
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
|
||||
int defaultScreen = X11::XDefaultScreen(display);
|
||||
|
||||
X11::XImage* image;
|
||||
image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
|
||||
color.pixel = XGetPixel(image, 0, 0);
|
||||
X11::XFree(image);
|
||||
|
||||
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
|
||||
|
||||
Color32 outputColor;
|
||||
outputColor.R = color.red / 256;
|
||||
outputColor.G = color.green / 256;
|
||||
outputColor.B = color.blue / 256;
|
||||
outputColor.A = 255;
|
||||
return outputColor;
|
||||
}
|
||||
|
||||
void OnScreenUtilsXEventCallback(void* eventPtr)
|
||||
{
|
||||
X11::XEvent* event = (X11::XEvent*) eventPtr;
|
||||
X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||
if (event->type == ButtonPress)
|
||||
{
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
||||
X11::XUngrabPointer(display, CurrentTime);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
|
||||
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
|
||||
|
||||
X11::Cursor cursor = XCreateFontCursor(display, 130);
|
||||
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
|
||||
if (grabbedPointer != GrabSuccess)
|
||||
{
|
||||
LOG(Error, "Failed to grab cursor for events.");
|
||||
X11::XFreeCursor(display, cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
X11::XFreeCursor(display, cursor);
|
||||
LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
|
||||
#elif PLATFORM_MAC
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
// TODO: implement ScreenUtilities for macOS
|
||||
return { 0, 0, 0, 255 };
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
// This is what C# calls to start the color picking sequence
|
||||
// This should stop mouse clicks from working for one click, and that click is on the selected color
|
||||
// There is a class called NSColorSample that might implement that for you, but maybe not.
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,32 +2,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
#include "Engine/Core/Math/Color32.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
|
||||
/// <summary>
|
||||
/// Platform-dependent screen utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pixel color at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="pos">Screen-space coordinate to read.</param>
|
||||
/// <returns>Pixel color at the specified coordinates.</returns>
|
||||
API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
|
||||
|
||||
/// <summary>
|
||||
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void PickColor();
|
||||
|
||||
/// <summary>
|
||||
/// Called when PickColor action is finished.
|
||||
/// </summary>
|
||||
API_EVENT() static Delegate<Color32> PickColorDone;
|
||||
};
|
||||
#include "Engine/Platform/ScreenUtilities.h"
|
||||
|
||||
@@ -250,6 +250,8 @@ namespace FlaxEditor.Utilities
|
||||
|
||||
internal static Int2 DrawCurveTicks(DrawCurveTick drawTick, float[] tickSteps, ref float[] tickStrengths, float min, float max, float pixelRange, float minDistanceBetweenTicks = 20, float maxDistanceBetweenTicks = 60)
|
||||
{
|
||||
if (pixelRange <= Mathf.Epsilon || maxDistanceBetweenTicks <= minDistanceBetweenTicks)
|
||||
return Int2.Zero;
|
||||
if (tickStrengths == null || tickStrengths.Length != tickSteps.Length)
|
||||
tickStrengths = new float[tickSteps.Length];
|
||||
|
||||
@@ -286,7 +288,7 @@ namespace FlaxEditor.Utilities
|
||||
continue;
|
||||
|
||||
// Draw all ticks
|
||||
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 1);
|
||||
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 2);
|
||||
var lStep = tickSteps[l];
|
||||
var lNextStep = tickSteps[l + 1];
|
||||
int startTick = Mathf.FloorToInt(min / lStep);
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace FlaxEditor.Viewport
|
||||
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Float2 MouseDelta => _mouseDelta;
|
||||
public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
|
||||
|
||||
@@ -158,18 +158,22 @@ namespace FlaxEditor.Viewport
|
||||
private float _movementSpeed;
|
||||
private float _minMovementSpeed;
|
||||
private float _maxMovementSpeed;
|
||||
#if !PLATFORM_SDL
|
||||
private float _mouseAccelerationScale;
|
||||
private bool _useMouseFiltering;
|
||||
private bool _useMouseAcceleration;
|
||||
#endif
|
||||
|
||||
// Input
|
||||
|
||||
internal bool _disableInputUpdate;
|
||||
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
|
||||
private int _deltaFilteringStep;
|
||||
private Float2 _startPos;
|
||||
#if !PLATFORM_SDL
|
||||
private Float2 _mouseDeltaLast;
|
||||
private int _deltaFilteringStep;
|
||||
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The previous input (from the previous update).
|
||||
@@ -525,10 +529,11 @@ namespace FlaxEditor.Viewport
|
||||
: base(task)
|
||||
{
|
||||
_editor = Editor.Instance;
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
_mouseAccelerationScale = 0.1f;
|
||||
_useMouseFiltering = false;
|
||||
_useMouseAcceleration = false;
|
||||
#endif
|
||||
_camera = camera;
|
||||
if (_camera != null)
|
||||
_camera.Viewport = this;
|
||||
@@ -1456,7 +1461,9 @@ namespace FlaxEditor.Viewport
|
||||
// Hide cursor and start tracking mouse movement
|
||||
win.StartTrackingMouse(false);
|
||||
win.Cursor = CursorType.Hidden;
|
||||
win.MouseMoveRelative += OnMouseMoveRelative;
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Center mouse position if it's too close to the edge
|
||||
var size = Size;
|
||||
var center = Float2.Round(size * 0.5f);
|
||||
@@ -1465,6 +1472,7 @@ namespace FlaxEditor.Viewport
|
||||
_viewMousePos = center;
|
||||
win.MousePosition = PointToWindow(_viewMousePos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1476,6 +1484,7 @@ namespace FlaxEditor.Viewport
|
||||
// Restore cursor and stop tracking mouse movement
|
||||
win.Cursor = CursorType.Default;
|
||||
win.EndTrackingMouse();
|
||||
win.MouseMoveRelative -= OnMouseMoveRelative;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1580,6 +1589,14 @@ namespace FlaxEditor.Viewport
|
||||
else
|
||||
EndMouseCapture();
|
||||
}
|
||||
#if PLATFORM_SDL
|
||||
bool useMouse = IsControllingMouse || true;
|
||||
_prevInput = _input;
|
||||
if (canUseInput && ContainsFocus)
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
_input.Clear();
|
||||
#else
|
||||
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
|
||||
_prevInput = _input;
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
@@ -1587,6 +1604,7 @@ namespace FlaxEditor.Viewport
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
_input.Clear();
|
||||
#endif
|
||||
|
||||
// Track controlling mouse state change
|
||||
bool wasControllingMouse = _prevInput.IsControllingMouse;
|
||||
@@ -1695,6 +1713,10 @@ namespace FlaxEditor.Viewport
|
||||
if (_input.IsControlDown)
|
||||
moveDelta *= 0.3f;
|
||||
|
||||
#if PLATFORM_SDL
|
||||
var mouseDelta = _mouseDelta;
|
||||
_mouseDelta = Float2.Zero;
|
||||
#else
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
var offset = _viewMousePos - _startPos;
|
||||
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
|
||||
@@ -1736,6 +1758,7 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
|
||||
_mouseDeltaLast = currentDelta;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update
|
||||
moveDelta *= dt * (60.0f * 4.0f);
|
||||
@@ -1744,12 +1767,14 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta *= new Float2(1, -1);
|
||||
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Move mouse back to the root position
|
||||
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
|
||||
{
|
||||
var center = PointToWindow(_startPos);
|
||||
win.MousePosition = center;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Change Ortho size on mouse scroll
|
||||
if (_isOrtho && !rmbWheel)
|
||||
@@ -1761,6 +1786,8 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
#else
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
@@ -1775,6 +1802,7 @@ namespace FlaxEditor.Viewport
|
||||
_mouseDelta = Float2.Zero;
|
||||
}
|
||||
_mouseDeltaLast = Float2.Zero;
|
||||
#endif
|
||||
|
||||
if (ContainsFocus)
|
||||
{
|
||||
@@ -1824,6 +1852,12 @@ namespace FlaxEditor.Viewport
|
||||
_input.MouseWheelDelta = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnMouseMoveRelative(ref Float2 mouseMotion)
|
||||
{
|
||||
_mouseDelta += mouseMotion;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
|
||||
@@ -54,24 +54,24 @@ namespace FlaxEditor.Windows.Assets
|
||||
[EditorOrder(10), EditorDisplay("General"), Tooltip("Material domain type.")]
|
||||
public MaterialDomain Domain;
|
||||
|
||||
[EditorOrder(20), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
|
||||
[EditorOrder(20), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
|
||||
public MaterialShadingModel ShadingModel;
|
||||
|
||||
[EditorOrder(30), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
|
||||
[EditorOrder(30), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
|
||||
public MaterialBlendMode BlendMode;
|
||||
|
||||
// Rendering
|
||||
|
||||
[EditorOrder(100), DefaultValue(CullMode.Normal), EditorDisplay("Rendering"), Tooltip("Defines the primitives culling mode used during geometry rendering.")]
|
||||
[EditorOrder(100), DefaultValue(CullMode.Normal), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Defines the primitives culling mode used during geometry rendering.")]
|
||||
public CullMode CullMode;
|
||||
|
||||
[EditorOrder(110), DefaultValue(false), EditorDisplay("Rendering"), Tooltip("If checked, geometry will be rendered in wireframe mode without solid triangles fill.")]
|
||||
[EditorOrder(110), DefaultValue(false), VisibleIf(nameof(IsStandardOrGUI)), EditorDisplay("Rendering"), Tooltip("If checked, geometry will be rendered in wireframe mode without solid triangles fill.")]
|
||||
public bool Wireframe;
|
||||
|
||||
[EditorOrder(120), DefaultValue(true), EditorDisplay("Rendering"), Tooltip("Enables performing depth test during material rendering.")]
|
||||
[EditorOrder(120), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Enables performing depth test during material rendering.")]
|
||||
public bool DepthTest;
|
||||
|
||||
[EditorOrder(130), DefaultValue(true), EditorDisplay("Rendering"), Tooltip("Enable writing to the depth buffer during material rendering.")]
|
||||
[EditorOrder(130), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Enable writing to the depth buffer during material rendering.")]
|
||||
public bool DepthWrite;
|
||||
|
||||
// Transparency
|
||||
@@ -111,13 +111,13 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
// Misc
|
||||
|
||||
[EditorOrder(400), DefaultValue(false), EditorDisplay("Misc"), Tooltip("If checked, material input normal will be assumed as world-space rather than tangent-space.")]
|
||||
[EditorOrder(400), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc"), Tooltip("If checked, material input normal will be assumed as world-space rather than tangent-space.")]
|
||||
public bool InputWorldSpaceNormal;
|
||||
|
||||
[EditorOrder(410), DefaultValue(false), EditorDisplay("Misc", "Dithered LOD Transition"), Tooltip("If checked, material uses dithered model LOD transition for smoother LODs switching.")]
|
||||
[EditorOrder(410), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc", "Dithered LOD Transition"), Tooltip("If checked, material uses dithered model LOD transition for smoother LODs switching.")]
|
||||
public bool DitheredLODTransition;
|
||||
|
||||
[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), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
||||
public float MaskThreshold;
|
||||
|
||||
[EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), VisibleIf(nameof(IsDecal)), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")]
|
||||
@@ -144,7 +144,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
private bool IsPostProcess => Domain == MaterialDomain.PostProcess;
|
||||
private bool IsDecal => Domain == MaterialDomain.Decal;
|
||||
private bool IsGUI => Domain == MaterialDomain.GUI;
|
||||
private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable;
|
||||
private bool IsStandardOrGUI => IsStandard || IsGUI;
|
||||
|
||||
/// <summary>
|
||||
/// Gathers parameters from the specified material.
|
||||
|
||||
@@ -1068,8 +1068,11 @@ namespace FlaxEditor.Windows
|
||||
|
||||
if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
|
||||
{
|
||||
// Make sure the cursor is always in the viewport when cursor is locked
|
||||
bool forceCenter = _cursorLockMode != CursorLockMode.None && !IsMouseOver;
|
||||
|
||||
// Center mouse in play mode
|
||||
if (CenterMouseOnFocus)
|
||||
if (CenterMouseOnFocus || forceCenter)
|
||||
{
|
||||
var center = PointToWindow(Size * 0.5f);
|
||||
Root.MousePosition = center;
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
|
||||
private string FormatCellBytes(object x)
|
||||
{
|
||||
return Utilities.Utils.FormatBytesCount((ulong)x);
|
||||
return Utilities.Utils.FormatBytesCount((ulong)(int)x);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -28,7 +28,7 @@ const Char* SplashScreenQuotes[] =
|
||||
#elif PLATFORM_LINUX
|
||||
TEXT("Try it on a Raspberry"),
|
||||
TEXT("Trying to exit vim"),
|
||||
TEXT("Sudo flax --loadproject"),
|
||||
TEXT("sudo flax --project HelloWorld.flaxproj"),
|
||||
#elif PLATFORM_MAC
|
||||
TEXT("don't compare Macbooks to oranges."),
|
||||
TEXT("Why does macbook heat up?\nBecause it doesn't have windows"),
|
||||
@@ -104,6 +104,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("You have my bow.\nAnd my axe!"),
|
||||
TEXT("To the bridge of Khazad-dum."),
|
||||
TEXT("One ring to rule them all.\nOne ring to find them."),
|
||||
TEXT("Where there's a whip, there's a way."),
|
||||
TEXT("That's what she said"),
|
||||
TEXT("We could be compiling shaders here"),
|
||||
TEXT("Hello There"),
|
||||
@@ -164,7 +165,7 @@ void SplashScreen::Show()
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowDragAndDrop = false;
|
||||
settings.IsTopmost = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.Type = WindowType::Utility;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
settings.StartPosition = WindowStartPosition::CenterScreen;
|
||||
|
||||
@@ -66,27 +66,23 @@ namespace AnimationUtils
|
||||
}
|
||||
|
||||
template<class T>
|
||||
FORCE_INLINE static void GetTangent(const T& a, const T& b, float length, T& result)
|
||||
FORCE_INLINE static void GetTangent(const T& value, const T& tangent, float tangentScale, T& result)
|
||||
{
|
||||
const float oneThird = 1.0f / 3.0f;
|
||||
result = a + b * (length * oneThird);
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& a, const Quaternion& b, float length, Quaternion& result)
|
||||
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& value, const Quaternion& tangent, float tangentScale, Quaternion& result)
|
||||
{
|
||||
const float oneThird = 1.0f / 3.0f;
|
||||
Quaternion::Slerp(a, b, oneThird, result);
|
||||
Quaternion::Slerp(value, tangent, 1.0f / 3.0f, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE void GetTangent<Transform>(const Transform& a, const Transform& b, float length, Transform& result)
|
||||
FORCE_INLINE void GetTangent<Transform>(const Transform& value, const Transform& tangent, float tangentScale, Transform& result)
|
||||
{
|
||||
const float oneThird = 1.0f / 3.0f;
|
||||
const float oneThirdLength = length * oneThird;
|
||||
result.Translation = a.Translation + b.Translation * oneThirdLength;
|
||||
Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation);
|
||||
result.Scale = a.Scale + (b.Scale - a.Scale) * oneThirdLength;
|
||||
GetTangent(value.Translation, tangent.Translation, tangentScale, result.Translation);
|
||||
GetTangent(value.Orientation, tangent.Orientation, tangentScale, result.Orientation);
|
||||
GetTangent(value.Scale, tangent.Scale, tangentScale, result.Scale);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine.Interop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
@@ -24,9 +26,9 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="tangent">The tangent.</param>
|
||||
/// <param name="lengthThird">The length divided by 3.</param>
|
||||
/// <param name="tangentScale">The tangent scale factor.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
void GetTangent(ref U value, ref U tangent, float lengthThird, out U result);
|
||||
void GetTangent(ref U value, ref U tangent, float tangentScale, out U result);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the linear interpolation at the specified alpha.
|
||||
@@ -67,7 +69,7 @@ namespace FlaxEngine
|
||||
IKeyframeAccess<Color32>,
|
||||
IKeyframeAccess<Color>
|
||||
{
|
||||
public void GetTangent(ref bool value, ref bool tangent, float lengthThird, out bool result)
|
||||
public void GetTangent(ref bool value, ref bool tangent, float tangentScale, out bool result)
|
||||
{
|
||||
result = value;
|
||||
}
|
||||
@@ -82,9 +84,9 @@ namespace FlaxEngine
|
||||
result = p0;
|
||||
}
|
||||
|
||||
public void GetTangent(ref int value, ref int tangent, float lengthThird, out int result)
|
||||
public void GetTangent(ref int value, ref int tangent, float tangentScale, out int result)
|
||||
{
|
||||
result = value + (int)(tangent * lengthThird);
|
||||
result = value + (int)(tangent * tangentScale);
|
||||
}
|
||||
|
||||
public void Linear(ref int a, ref int b, float alpha, out int result)
|
||||
@@ -102,9 +104,9 @@ namespace FlaxEngine
|
||||
result = Mathf.Lerp(p012, p123, alpha);
|
||||
}
|
||||
|
||||
public void GetTangent(ref double value, ref double tangent, float lengthThird, out double result)
|
||||
public void GetTangent(ref double value, ref double tangent, float tangentScale, out double result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref double a, ref double b, float alpha, out double result)
|
||||
@@ -122,9 +124,9 @@ namespace FlaxEngine
|
||||
result = Mathf.Lerp(p012, p123, alpha);
|
||||
}
|
||||
|
||||
public void GetTangent(ref float value, ref float tangent, float lengthThird, out float result)
|
||||
public void GetTangent(ref float value, ref float tangent, float tangentScale, out float result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref float a, ref float b, float alpha, out float result)
|
||||
@@ -142,9 +144,9 @@ namespace FlaxEngine
|
||||
result = Mathf.Lerp(p012, p123, alpha);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float lengthThird, out Vector2 result)
|
||||
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float tangentScale, out Vector2 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Vector2 a, ref Vector2 b, float alpha, out Vector2 result)
|
||||
@@ -162,9 +164,9 @@ namespace FlaxEngine
|
||||
Vector2.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float lengthThird, out Vector3 result)
|
||||
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float tangentScale, out Vector3 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Vector3 a, ref Vector3 b, float alpha, out Vector3 result)
|
||||
@@ -182,9 +184,9 @@ namespace FlaxEngine
|
||||
Vector3.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float lengthThird, out Vector4 result)
|
||||
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float tangentScale, out Vector4 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Vector4 a, ref Vector4 b, float alpha, out Vector4 result)
|
||||
@@ -202,9 +204,9 @@ namespace FlaxEngine
|
||||
Vector4.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Float2 value, ref Float2 tangent, float lengthThird, out Float2 result)
|
||||
public void GetTangent(ref Float2 value, ref Float2 tangent, float tangentScale, out Float2 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Float2 a, ref Float2 b, float alpha, out Float2 result)
|
||||
@@ -222,9 +224,9 @@ namespace FlaxEngine
|
||||
Float2.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Float3 value, ref Float3 tangent, float lengthThird, out Float3 result)
|
||||
public void GetTangent(ref Float3 value, ref Float3 tangent, float tangentScale, out Float3 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Float3 a, ref Float3 b, float alpha, out Float3 result)
|
||||
@@ -242,9 +244,9 @@ namespace FlaxEngine
|
||||
Float3.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Float4 value, ref Float4 tangent, float lengthThird, out Float4 result)
|
||||
public void GetTangent(ref Float4 value, ref Float4 tangent, float tangentScale, out Float4 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Float4 a, ref Float4 b, float alpha, out Float4 result)
|
||||
@@ -262,9 +264,9 @@ namespace FlaxEngine
|
||||
Float4.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Double2 value, ref Double2 tangent, float lengthThird, out Double2 result)
|
||||
public void GetTangent(ref Double2 value, ref Double2 tangent, float tangentScale, out Double2 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Double2 a, ref Double2 b, float alpha, out Double2 result)
|
||||
@@ -282,9 +284,9 @@ namespace FlaxEngine
|
||||
Double2.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Double3 value, ref Double3 tangent, float lengthThird, out Double3 result)
|
||||
public void GetTangent(ref Double3 value, ref Double3 tangent, float tangentScale, out Double3 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Double3 a, ref Double3 b, float alpha, out Double3 result)
|
||||
@@ -302,9 +304,9 @@ namespace FlaxEngine
|
||||
Double3.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Double4 value, ref Double4 tangent, float lengthThird, out Double4 result)
|
||||
public void GetTangent(ref Double4 value, ref Double4 tangent, float tangentScale, out Double4 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Double4 a, ref Double4 b, float alpha, out Double4 result)
|
||||
@@ -322,7 +324,7 @@ namespace FlaxEngine
|
||||
Double4.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float lengthThird, out Quaternion result)
|
||||
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float tangentScale, out Quaternion result)
|
||||
{
|
||||
Quaternion.Slerp(ref value, ref tangent, 1.0f / 3.0f, out result);
|
||||
}
|
||||
@@ -342,9 +344,9 @@ namespace FlaxEngine
|
||||
Quaternion.Slerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Color32 value, ref Color32 tangent, float lengthThird, out Color32 result)
|
||||
public void GetTangent(ref Color32 value, ref Color32 tangent, float tangentScale, out Color32 result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Color32 a, ref Color32 b, float alpha, out Color32 result)
|
||||
@@ -362,9 +364,9 @@ namespace FlaxEngine
|
||||
Color32.Lerp(ref p012, ref p123, alpha, out result);
|
||||
}
|
||||
|
||||
public void GetTangent(ref Color value, ref Color tangent, float lengthThird, out Color result)
|
||||
public void GetTangent(ref Color value, ref Color tangent, float tangentScale, out Color result)
|
||||
{
|
||||
result = value + tangent * lengthThird;
|
||||
result = value + tangent * tangentScale;
|
||||
}
|
||||
|
||||
public void Linear(ref Color a, ref Color b, float alpha, out Color result)
|
||||
@@ -454,6 +456,40 @@ namespace FlaxEngine
|
||||
time = end;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||
/// </summary>
|
||||
/// <param name="keyframes">The keyframes array.</param>
|
||||
/// <returns>The raw keyframes data.</returns>
|
||||
protected static unsafe byte[] MarshalKeyframes<Keyframe>(Keyframe[] keyframes)
|
||||
{
|
||||
if (keyframes == null || keyframes.Length == 0)
|
||||
return null;
|
||||
var keyframeSize = Unsafe.SizeOf<Keyframe>();
|
||||
var result = new byte[keyframes.Length * keyframeSize];
|
||||
fixed (byte* resultPtr = result)
|
||||
{
|
||||
var keyframesHandle = ManagedHandle.Alloc(keyframes, GCHandleType.Pinned);
|
||||
var keyframesPtr = Marshal.UnsafeAddrOfPinnedArrayElement(keyframes, 0);
|
||||
Buffer.MemoryCopy((void*)keyframesPtr, resultPtr, (uint)result.Length, (uint)result.Length);
|
||||
keyframesHandle.Free();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||
/// </summary>
|
||||
/// <param name="raw">The raw keyframes data.</param>
|
||||
/// <returns>The keyframes array.</returns>
|
||||
protected static unsafe Keyframe[] MarshalKeyframes<Keyframe>(byte[] raw) where Keyframe : struct
|
||||
{
|
||||
if (raw == null || raw.Length == 0)
|
||||
return null;
|
||||
fixed (byte* rawPtr = raw)
|
||||
return MemoryMarshal.Cast<byte, Keyframe>(new Span<byte>(rawPtr, raw.Length)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -709,6 +745,30 @@ namespace FlaxEngine
|
||||
leftKey = Mathf.Max(0, start - 1);
|
||||
rightKey = Mathf.Min(start, Keyframes.Length - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||
/// </summary>
|
||||
/// <param name="curve">The curve to copy.</param>
|
||||
/// <returns>The raw keyframes data.</returns>
|
||||
public static unsafe implicit operator byte[](LinearCurve<T> curve)
|
||||
{
|
||||
if (curve == null)
|
||||
return null;
|
||||
return MarshalKeyframes<Keyframe>(curve.Keyframes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||
/// </summary>
|
||||
/// <param name="raw">The raw keyframes data.</param>
|
||||
/// <returns>The curve.</returns>
|
||||
public static unsafe implicit operator LinearCurve<T>(byte[] raw)
|
||||
{
|
||||
if (raw == null || raw.Length == 0)
|
||||
return null;
|
||||
return new LinearCurve<T>(MarshalKeyframes<Keyframe>(raw));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -860,9 +920,9 @@ namespace FlaxEngine
|
||||
|
||||
// Evaluate the key at the curve
|
||||
result.Time = leftKey.Time + length * t;
|
||||
float lengthThird = length / 3.0f;
|
||||
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
|
||||
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
|
||||
float tangentScale = length / 3.0f;
|
||||
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
|
||||
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
|
||||
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result.Value);
|
||||
result.TangentIn = leftKey.TangentOut;
|
||||
result.TangentOut = rightKey.TangentIn;
|
||||
@@ -895,9 +955,9 @@ namespace FlaxEngine
|
||||
float t = Mathf.NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length;
|
||||
|
||||
// Evaluate the value at the curve
|
||||
float lengthThird = length / 3.0f;
|
||||
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
|
||||
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
|
||||
float tangentScale = length / 3.0f;
|
||||
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
|
||||
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
|
||||
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result);
|
||||
}
|
||||
|
||||
@@ -1000,5 +1060,29 @@ namespace FlaxEngine
|
||||
leftKey = Mathf.Max(0, start - 1);
|
||||
rightKey = Mathf.Min(start, Keyframes.Length - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||
/// </summary>
|
||||
/// <param name="curve">The curve to copy.</param>
|
||||
/// <returns>The raw keyframes data.</returns>
|
||||
public static unsafe implicit operator byte[](BezierCurve<T> curve)
|
||||
{
|
||||
if (curve == null)
|
||||
return null;
|
||||
return MarshalKeyframes<Keyframe>(curve.Keyframes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||
/// </summary>
|
||||
/// <param name="raw">The raw keyframes data.</param>
|
||||
/// <returns>The curve.</returns>
|
||||
public static unsafe implicit operator BezierCurve<T>(byte[] raw)
|
||||
{
|
||||
if (raw == null || raw.Length == 0)
|
||||
return null;
|
||||
return new BezierCurve<T>(MarshalKeyframes<Keyframe>(raw));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,16 +247,18 @@ public:
|
||||
static void Interpolate(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
const float tangentScale = length / 3.0f;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
|
||||
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
||||
}
|
||||
|
||||
static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
const float tangentScale = length / 3.0f;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
|
||||
AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
||||
}
|
||||
|
||||
@@ -264,8 +266,9 @@ public:
|
||||
{
|
||||
result.Time = a.Time + length * alpha;
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
const float tangentScale = length / 3.0f;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
|
||||
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value);
|
||||
result.TangentIn = a.TangentOut;
|
||||
result.TangentOut = b.TangentIn;
|
||||
@@ -498,7 +501,7 @@ protected:
|
||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of a curve.
|
||||
/// </summary>
|
||||
template<class T, typename KeyFrame = LinearCurveKeyframe<T>>
|
||||
class Curve : public CurveBase<T, KeyFrame>
|
||||
API_CLASS(InBuild, Template, MarshalAs=Span<byte>) class Curve : public CurveBase<T, KeyFrame>
|
||||
{
|
||||
public:
|
||||
typedef CurveBase<T, KeyFrame> Base;
|
||||
@@ -760,28 +763,42 @@ public:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||
Curve& operator=(const Span<byte>& raw)
|
||||
{
|
||||
ASSERT((raw.Length() % sizeof(KeyFrame)) == 0);
|
||||
const int32 count = raw.Length() / sizeof(KeyFrame);
|
||||
_keyframes.Resize(count, false);
|
||||
Platform::MemoryCopy(_keyframes.Get(), raw.Get(), sizeof(KeyFrame) * count);
|
||||
return *this;
|
||||
}
|
||||
operator Span<byte>()
|
||||
{
|
||||
return Span<byte>((const byte*)_keyframes.Get(), _keyframes.Count() * sizeof(KeyFrame));
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// An animation spline represented by a set of keyframes, each representing a value point.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
using StepCurve = Curve<T, StepCurveKeyframe<T>>;
|
||||
API_TYPEDEF() using StepCurve = Curve<T, StepCurveKeyframe<T>>;
|
||||
|
||||
/// <summary>
|
||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
using LinearCurve = Curve<T, LinearCurveKeyframe<T>>;
|
||||
API_TYPEDEF() using LinearCurve = Curve<T, LinearCurveKeyframe<T>>;
|
||||
|
||||
/// <summary>
|
||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of a cubic hermite curve.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
using HermiteCurve = Curve<T, HermiteCurveKeyframe<T>>;
|
||||
API_TYPEDEF() using HermiteCurve = Curve<T, HermiteCurveKeyframe<T>>;
|
||||
|
||||
/// <summary>
|
||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of Bezier curve.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
using BezierCurve = Curve<T, BezierCurveKeyframe<T>>;
|
||||
API_TYPEDEF() using BezierCurve = Curve<T, BezierCurveKeyframe<T>>;
|
||||
|
||||
@@ -2263,6 +2263,14 @@ void VisualScript::GetMethodSignature(int32 index, String& name, byte& flags, St
|
||||
}
|
||||
}
|
||||
|
||||
Variant VisualScript::InvokeMethod(int32 index, const Variant& instance, Span<Variant> parameters) const
|
||||
{
|
||||
auto& method = _methods[index];
|
||||
Variant result;
|
||||
VisualScriptingModule.InvokeMethod((void*)&method, instance, parameters, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Span<byte> VisualScript::GetMetaData(int32 typeID)
|
||||
{
|
||||
auto meta = Graph.Meta.GetEntry(typeID);
|
||||
|
||||
@@ -267,6 +267,9 @@ public:
|
||||
// Gets the signature data of the method.
|
||||
API_FUNCTION() void GetMethodSignature(int32 index, API_PARAM(Out) String& name, API_PARAM(Out) byte& flags, API_PARAM(Out) String& returnTypeName, API_PARAM(Out) Array<String>& paramNames, API_PARAM(Out) Array<String>& paramTypeNames, API_PARAM(Out) Array<bool>& paramOuts);
|
||||
|
||||
// Invokes the method.
|
||||
API_FUNCTION() Variant InvokeMethod(int32 index, const Variant& instance, Span<Variant> parameters) const;
|
||||
|
||||
// Gets the metadata of the script surface.
|
||||
API_FUNCTION() Span<byte> GetMetaData(int32 typeID);
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
new BuildTarget
|
||||
{
|
||||
Name = "Windows 64bit",
|
||||
Output = "Output\\Win64",
|
||||
Name = "Windows",
|
||||
Output = "Output\\Windows",
|
||||
Platform = BuildPlatform.Windows64,
|
||||
Mode = BuildConfiguration.Development,
|
||||
},
|
||||
@@ -35,8 +35,8 @@ namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
new BuildTarget
|
||||
{
|
||||
Name = "Windows 64bit",
|
||||
Output = "Output\\Win64",
|
||||
Name = "Windows",
|
||||
Output = "Output\\Windows",
|
||||
Platform = BuildPlatform.Windows64,
|
||||
Mode = BuildConfiguration.Release,
|
||||
},
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
#include <iostream>
|
||||
|
||||
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH)
|
||||
#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_WINDOWS && PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE))
|
||||
|
||||
namespace
|
||||
{
|
||||
bool LogAfterInit = false, IsDuringLog = false;
|
||||
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
|
||||
bool IsWindowsSingleNewLineChar = false;
|
||||
#endif
|
||||
int LogTotalErrorsCnt = 0;
|
||||
int32 LogTotalWriteSize = 0;
|
||||
FileWriteStream* LogFile = nullptr;
|
||||
CriticalSection LogLocker;
|
||||
DateTime LogStartTime;
|
||||
@@ -86,6 +91,11 @@ bool Log::Logger::Init()
|
||||
}
|
||||
LogTotalErrorsCnt = 0;
|
||||
LogAfterInit = true;
|
||||
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
|
||||
String envVar;
|
||||
Platform::GetEnvironmentVariable(TEXT("GITHUB_ACTION"), envVar);
|
||||
IsWindowsSingleNewLineChar = envVar.HasChars();
|
||||
#endif
|
||||
|
||||
// Write BOM (UTF-16 (LE); BOM: FF FE)
|
||||
byte bom[] = { 0xFF, 0xFE };
|
||||
@@ -127,6 +137,11 @@ void Log::Logger::Write(const StringView& msg)
|
||||
printf("%s", ansi.Get());
|
||||
#else
|
||||
std::wcout.write(ptr, length);
|
||||
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
|
||||
if (IsWindowsSingleNewLineChar)
|
||||
std::wcout.write(TEXT("\n"), 1); // Github Actions show logs with duplicated new-line characters so skip \r
|
||||
else
|
||||
#endif
|
||||
std::wcout.write(TEXT(PLATFORM_LINE_TERMINATOR), ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1);
|
||||
#endif
|
||||
}
|
||||
@@ -135,10 +150,17 @@ void Log::Logger::Write(const StringView& msg)
|
||||
Platform::Log(msg);
|
||||
|
||||
// Write message to log file
|
||||
if (LogAfterInit)
|
||||
constexpr int32 LogMaxWriteSize = 1 * 1024 * 1024; // 1GB
|
||||
if (LogAfterInit && LogTotalWriteSize < LogMaxWriteSize)
|
||||
{
|
||||
LogTotalWriteSize += length;
|
||||
LogFile->WriteBytes(ptr, length * sizeof(Char));
|
||||
LogFile->WriteBytes(TEXT(PLATFORM_LINE_TERMINATOR), (ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1) * sizeof(Char));
|
||||
if (LogTotalWriteSize >= LogMaxWriteSize)
|
||||
{
|
||||
StringView endMessage(TEXT("Trimming log file.\n\n"));
|
||||
LogFile->WriteBytes(endMessage.Get(), endMessage.Length() * sizeof(Char));
|
||||
}
|
||||
#if LOG_ENABLE_AUTO_FLUSH
|
||||
LogFile->Flush();
|
||||
#endif
|
||||
|
||||
@@ -124,9 +124,9 @@ void BoundingBox::Transform(const BoundingBox& box, const Matrix& matrix, Boundi
|
||||
const auto ya = up * box.Minimum.Y;
|
||||
const auto yb = up * box.Maximum.Y;
|
||||
|
||||
const auto backward = matrix.GetBackward();
|
||||
const auto za = backward * box.Minimum.Z;
|
||||
const auto zb = backward * box.Maximum.Z;
|
||||
const auto forward = matrix.GetForward();
|
||||
const auto za = forward * box.Minimum.Z;
|
||||
const auto zb = forward * box.Maximum.Z;
|
||||
|
||||
const auto translation = matrix.GetTranslation();
|
||||
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + translation;
|
||||
@@ -146,9 +146,9 @@ void BoundingBox::Transform(const BoundingBox& box, const ::Transform& transform
|
||||
const auto ya = up * box.Minimum.Y;
|
||||
const auto yb = up * box.Maximum.Y;
|
||||
|
||||
const auto backward = Float3::Transform(Float3::Backward, transform.Orientation);
|
||||
const auto za = backward * box.Minimum.Z;
|
||||
const auto zb = backward * box.Maximum.Z;
|
||||
const auto forward = Float3::Transform(Float3::Forward, transform.Orientation);
|
||||
const auto za = forward * box.Minimum.Z;
|
||||
const auto zb = forward * box.Maximum.Z;
|
||||
|
||||
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + transform.Translation;
|
||||
const auto max = Vector3::Max(xa, xb) + Vector3::Max(ya, yb) + Vector3::Max(za, zb) + transform.Translation;
|
||||
|
||||
@@ -474,9 +474,9 @@ namespace FlaxEngine
|
||||
var ya = up * box.Minimum.Y;
|
||||
var yb = up * box.Maximum.Y;
|
||||
|
||||
Double3 backward = transform.Backward;
|
||||
var za = backward * box.Minimum.Z;
|
||||
var zb = backward * box.Maximum.Z;
|
||||
Double3 forward = transform.Forward;
|
||||
var za = forward * box.Minimum.Z;
|
||||
var zb = forward * box.Maximum.Z;
|
||||
|
||||
var translation = transform.TranslationVector;
|
||||
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + translation;
|
||||
@@ -514,9 +514,9 @@ namespace FlaxEngine
|
||||
var ya = up * box.Minimum.Y;
|
||||
var yb = up * box.Maximum.Y;
|
||||
|
||||
Double3 backward = transform.Backward;
|
||||
var za = backward * box.Minimum.Z;
|
||||
var zb = backward * box.Maximum.Z;
|
||||
Double3 forward = transform.Forward;
|
||||
var za = forward * box.Minimum.Z;
|
||||
var zb = forward * box.Maximum.Z;
|
||||
|
||||
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + transform.Translation;
|
||||
var max = Vector3.Max(xa, xb) + Vector3.Max(ya, yb) + Vector3.Max(za, zb) + transform.Translation;
|
||||
|
||||
@@ -136,12 +136,12 @@ void Matrix::Decompose(Float3& scale, Matrix3x3& rotation, Float3& translation)
|
||||
const auto right = Float3::Cross(up, at);
|
||||
rotation.SetRight(right);
|
||||
rotation.SetUp(up);
|
||||
rotation.SetBackward(at);
|
||||
rotation.SetForward(at);
|
||||
|
||||
// In case of reflexions
|
||||
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
|
||||
scale.Y = Float3::Dot(up, GetUp()) > 0.0f ? scale.Y : -scale.Y;
|
||||
scale.Z = Float3::Dot(at, GetBackward()) > 0.0f ? scale.Z : -scale.Z;
|
||||
scale.Z = Float3::Dot(at, GetForward()) > 0.0f ? scale.Z : -scale.Z;
|
||||
}
|
||||
|
||||
void Matrix::Decompose(Float3& scale, Matrix& rotation, Float3& translation) const
|
||||
|
||||
@@ -225,7 +225,7 @@ void Matrix3x3::Decompose(Float3& scale, Matrix3x3& rotation) const
|
||||
const auto right = Float3::Cross(up, at);
|
||||
rotation.SetRight(right);
|
||||
rotation.SetUp(up);
|
||||
rotation.SetBackward(at);
|
||||
rotation.SetForward(at);
|
||||
|
||||
// In case of reflexions
|
||||
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
|
||||
|
||||
@@ -1824,6 +1824,7 @@ Variant::operator Float4() const
|
||||
return Float4(*(Float3*)AsData, 0.0f);
|
||||
case VariantType::Float4:
|
||||
case VariantType::Color:
|
||||
case VariantType::Quaternion:
|
||||
return *(Float4*)AsData;
|
||||
case VariantType::Double2:
|
||||
return Float4(AsDouble2(), 0.0f, 0.0f);
|
||||
|
||||
@@ -94,6 +94,13 @@ struct DebugLine
|
||||
float TimeLeft;
|
||||
};
|
||||
|
||||
struct DebugGeometryBuffer
|
||||
{
|
||||
GPUBuffer* Buffer;
|
||||
float TimeLeft;
|
||||
Matrix Transform;
|
||||
};
|
||||
|
||||
struct DebugTriangle
|
||||
{
|
||||
Float3 V0;
|
||||
@@ -122,12 +129,9 @@ struct DebugText3D
|
||||
float TimeLeft;
|
||||
};
|
||||
|
||||
PACK_STRUCT(struct Vertex {
|
||||
Float3 Position;
|
||||
Color32 Color;
|
||||
});
|
||||
typedef DebugDraw::Vertex Vertex;
|
||||
|
||||
GPU_CB_STRUCT(Data {
|
||||
GPU_CB_STRUCT(ShaderData {
|
||||
Matrix ViewProjection;
|
||||
Float2 Padding;
|
||||
float ClipPosZBias;
|
||||
@@ -231,6 +235,7 @@ void TeleportList(const Float3& delta, Array<DebugText3D>& list)
|
||||
|
||||
struct DebugDrawData
|
||||
{
|
||||
Array<DebugGeometryBuffer> GeometryBuffers;
|
||||
Array<DebugLine> DefaultLines;
|
||||
Array<Vertex> OneFrameLines;
|
||||
Array<DebugTriangle> DefaultTriangles;
|
||||
@@ -244,7 +249,7 @@ struct DebugDrawData
|
||||
|
||||
inline int32 Count() const
|
||||
{
|
||||
return LinesCount() + TrianglesCount() + TextCount();
|
||||
return LinesCount() + TrianglesCount() + TextCount() + GeometryBuffers.Count();
|
||||
}
|
||||
|
||||
inline int32 LinesCount() const
|
||||
@@ -280,6 +285,7 @@ struct DebugDrawData
|
||||
|
||||
inline void Update(float deltaTime)
|
||||
{
|
||||
UpdateList(deltaTime, GeometryBuffers);
|
||||
UpdateList(deltaTime, DefaultLines);
|
||||
UpdateList(deltaTime, DefaultTriangles);
|
||||
UpdateList(deltaTime, DefaultWireTriangles);
|
||||
@@ -784,7 +790,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
||||
|
||||
// Update constant buffer
|
||||
const auto cb = DebugDrawShader->GetShader()->GetCB(0);
|
||||
Data data;
|
||||
ShaderData data;
|
||||
Matrix vp;
|
||||
Matrix::Multiply(view.View, view.Projection, vp);
|
||||
Matrix::Transpose(vp, data.ViewProjection);
|
||||
@@ -830,6 +836,22 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
||||
context->Draw(depthTestTriangles.StartVertex, depthTestTriangles.VertexCount);
|
||||
}
|
||||
|
||||
// Geometries
|
||||
for (auto& geometry : Context->DebugDrawDepthTest.GeometryBuffers)
|
||||
{
|
||||
auto tmp = data;
|
||||
Matrix mvp;
|
||||
Matrix::Multiply(geometry.Transform, vp, mvp);
|
||||
Matrix::Transpose(mvp, tmp.ViewProjection);
|
||||
context->UpdateCB(cb, &tmp);
|
||||
auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault;
|
||||
context->SetState(state->Get(enableDepthWrite, true));
|
||||
context->BindVB(ToSpan(&geometry.Buffer, 1));
|
||||
context->Draw(0, geometry.Buffer->GetElementsCount());
|
||||
}
|
||||
if (Context->DebugDrawDepthTest.GeometryBuffers.HasItems())
|
||||
context->UpdateCB(cb, &data);
|
||||
|
||||
if (data.EnableDepthTest)
|
||||
context->UnBindSR(0);
|
||||
}
|
||||
@@ -862,6 +884,19 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
||||
context->BindVB(ToSpan(&vb, 1));
|
||||
context->Draw(defaultTriangles.StartVertex, defaultTriangles.VertexCount);
|
||||
}
|
||||
|
||||
// Geometries
|
||||
for (auto& geometry : Context->DebugDrawDefault.GeometryBuffers)
|
||||
{
|
||||
auto tmp = data;
|
||||
Matrix mvp;
|
||||
Matrix::Multiply(geometry.Transform, vp, mvp);
|
||||
Matrix::Transpose(mvp, tmp.ViewProjection);
|
||||
context->UpdateCB(cb, &tmp);
|
||||
context->SetState(DebugDrawPsLinesDefault.Get(false, false));
|
||||
context->BindVB(ToSpan(&geometry.Buffer, 1));
|
||||
context->Draw(0, geometry.Buffer->GetElementsCount());
|
||||
}
|
||||
}
|
||||
|
||||
// Text
|
||||
@@ -1088,6 +1123,24 @@ void DebugDraw::DrawLines(const Span<Float3>& lines, const Matrix& transform, co
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDraw::DrawLines(GPUBuffer* lines, const Matrix& transform, float duration, bool depthTest)
|
||||
{
|
||||
if (lines == nullptr || lines->GetSize() == 0)
|
||||
return;
|
||||
if (lines->GetSize() % (sizeof(Vertex) * 2) != 0)
|
||||
{
|
||||
DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array");
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw lines
|
||||
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
||||
auto& geometry = debugDrawData.GeometryBuffers.AddOne();
|
||||
geometry.Buffer = lines;
|
||||
geometry.TimeLeft = duration;
|
||||
geometry.Transform = transform * Matrix::Translation(-Context->Origin);
|
||||
}
|
||||
|
||||
void DebugDraw::DrawLines(const Array<Float3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
||||
{
|
||||
DrawLines(Span<Float3>(lines.Get(), lines.Count()), transform, color, duration, depthTest);
|
||||
@@ -2147,6 +2200,7 @@ void DebugDraw::DrawText(const StringView& text, const Transform& transform, con
|
||||
|
||||
void DebugDraw::Clear(void* context)
|
||||
{
|
||||
DebugDraw::UpdateContext(context, MAX_float);
|
||||
UpdateContext(context, MAX_float);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
#include "Engine/Core/Math/Color.h"
|
||||
#include "Engine/Core/Math/Color32.h"
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Types/Span.h"
|
||||
|
||||
struct RenderView;
|
||||
@@ -14,6 +16,7 @@ class Light;
|
||||
struct RenderContext;
|
||||
class GPUTextureView;
|
||||
class GPUContext;
|
||||
class GPUBuffer;
|
||||
class RenderTask;
|
||||
class SceneRenderTask;
|
||||
class Actor;
|
||||
@@ -26,6 +29,14 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
|
||||
|
||||
/// <summary>
|
||||
/// Vertex data for debug shapes.
|
||||
/// </summary>
|
||||
PACK_STRUCT(struct Vertex {
|
||||
Float3 Position;
|
||||
Color32 Color;
|
||||
});
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Allocates the context for Debug Drawing. Can be use to redirect debug shapes collecting to a separate container (instead of global state).
|
||||
@@ -175,6 +186,15 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
||||
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
|
||||
API_FUNCTION() static void DrawLines(const Span<Float3>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the lines using the provided vertex buffer that contains pairs of Vertex elements. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
|
||||
/// </summary>
|
||||
/// <param name="lines">The GPU buffer with vertices for lines (must have multiple of 2 elements).</param>
|
||||
/// <param name="transform">The custom matrix used to transform all line vertices.</param>
|
||||
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
|
||||
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
|
||||
API_FUNCTION() static void DrawLines(GPUBuffer* lines, const Matrix& transform, float duration = 0.0f, bool depthTest = true);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
|
||||
/// </summary>
|
||||
@@ -691,9 +711,9 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
||||
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Clear all debug draw displayed on sceen.
|
||||
/// Clears all debug shapes displayed on screen.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <param name="context">The context.</param>
|
||||
API_FUNCTION() static void Clear(void* context = nullptr);
|
||||
};
|
||||
|
||||
|
||||
@@ -145,6 +145,12 @@ bool CommandLine::Parse(const Char* cmdLine)
|
||||
PARSE_BOOL_SWITCH("-monolog ", MonoLog);
|
||||
PARSE_BOOL_SWITCH("-mute ", Mute);
|
||||
PARSE_BOOL_SWITCH("-lowdpi ", LowDPI);
|
||||
|
||||
#if PLATFORM_LINUX && PLATFORM_SDL
|
||||
PARSE_BOOL_SWITCH("-wayland ", Wayland);
|
||||
PARSE_BOOL_SWITCH("-x11 ", X11);
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
PARSE_BOOL_SWITCH("-clearcache ", ClearCache);
|
||||
PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache);
|
||||
@@ -154,6 +160,7 @@ bool CommandLine::Parse(const Char* cmdLine)
|
||||
PARSE_ARG_SWITCH("-build ", Build);
|
||||
PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile);
|
||||
PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug);
|
||||
PARSE_BOOL_SWITCH("-exit ", Exit);
|
||||
PARSE_ARG_OPT_SWITCH("-play ", Play);
|
||||
#endif
|
||||
#if USE_EDITOR || !BUILD_RELEASE
|
||||
|
||||
@@ -127,6 +127,20 @@ public:
|
||||
/// </summary>
|
||||
Nullable<bool> LowDPI;
|
||||
|
||||
#if PLATFORM_LINUX && PLATFORM_SDL
|
||||
|
||||
/// <summary>
|
||||
/// -wayland (prefer Wayland over X11 as display server)
|
||||
/// </summary>
|
||||
Nullable<bool> Wayland;
|
||||
|
||||
/// <summary>
|
||||
/// -x11 (prefer X11 over Wayland as display server)
|
||||
/// </summary>
|
||||
Nullable<bool> X11;
|
||||
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// -project !path! (Startup project path)
|
||||
@@ -168,6 +182,11 @@ public:
|
||||
/// </summary>
|
||||
Nullable<bool> ShaderDebug;
|
||||
|
||||
/// <summary>
|
||||
/// -exit (exits the editor after startup and performing all queued actions). Usefull when invoking editor from CL/CD.
|
||||
/// </summary>
|
||||
Nullable<bool> Exit;
|
||||
|
||||
/// <summary>
|
||||
/// -play !guid! ( Scene to play, can be empty to use default )
|
||||
/// </summary>
|
||||
|
||||
@@ -95,13 +95,14 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
CommandLine::Options.Std = true;
|
||||
#endif
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
|
||||
if (Platform::Init())
|
||||
{
|
||||
Platform::Fatal(TEXT("Cannot init platform."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
Time::StartupTime = DateTime::Now();
|
||||
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Engine/Core/Types/Nullable.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
@@ -13,10 +15,14 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#endif
|
||||
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
namespace
|
||||
{
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
bool LastGameViewportFocus = false;
|
||||
}
|
||||
|
||||
class ScreenService : public EngineService
|
||||
{
|
||||
@@ -100,11 +106,25 @@ void Screen::SetCursorVisible(const bool value)
|
||||
#else
|
||||
const auto win = Engine::MainWindow;
|
||||
#endif
|
||||
bool focused = false;
|
||||
if (win && Engine::HasGameViewportFocus())
|
||||
{
|
||||
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
|
||||
focused = true;
|
||||
}
|
||||
else if (win)
|
||||
win->SetCursor(CursorType::Default);
|
||||
CursorVisible = value;
|
||||
|
||||
// Just enable relative mode when cursor is constrained and not visible
|
||||
if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(true, win);
|
||||
}
|
||||
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(false, win);
|
||||
}
|
||||
}
|
||||
|
||||
CursorLockMode Screen::GetCursorLock()
|
||||
@@ -133,6 +153,17 @@ void Screen::SetCursorLock(CursorLockMode mode)
|
||||
win->EndClippingCursor();
|
||||
}
|
||||
CursorLock = mode;
|
||||
|
||||
// Just enable relative mode when cursor is constrained and not visible
|
||||
bool focused = win && Engine::HasGameViewportFocus();
|
||||
if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(true, win);
|
||||
}
|
||||
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(false, win);
|
||||
}
|
||||
}
|
||||
|
||||
GameWindowMode Screen::GetGameWindowMode()
|
||||
@@ -190,7 +221,11 @@ void ScreenService::Update()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Sync current cursor state in Editor (eg. when viewport focus can change)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
const auto win = Editor::Managed->GetGameWindow(true);
|
||||
bool gameViewportFocus = win && Engine::HasGameViewportFocus();
|
||||
if (gameViewportFocus != LastGameViewportFocus)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
LastGameViewportFocus = gameViewportFocus;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
|
||||
#define GPU_TASKS_USE_DEDICATED_CONTEXT 0
|
||||
|
||||
@@ -36,7 +37,8 @@ GPUTasksContext::~GPUTasksContext()
|
||||
auto task = tasks[i];
|
||||
if (task->GetSyncPoint() <= _currentSyncPoint && task->GetState() != TaskState::Finished)
|
||||
{
|
||||
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
||||
if (!Globals::IsRequestingExit)
|
||||
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
||||
task->CancelSync();
|
||||
}
|
||||
}
|
||||
@@ -60,7 +62,8 @@ void GPUTasksContext::OnCancelSync(GPUTask* task)
|
||||
|
||||
_tasksDone.Remove(task);
|
||||
|
||||
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
||||
if (!Globals::IsRequestingExit)
|
||||
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
||||
}
|
||||
|
||||
void GPUTasksContext::OnFrameBegin()
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "GPUResourceProperty.h"
|
||||
#include "GPUBufferDescription.h"
|
||||
#include "PixelFormatExtensions.h"
|
||||
#include "RenderTask.h"
|
||||
#include "Async/Tasks/GPUCopyResourceTask.h"
|
||||
#include "Engine/Core/Utilities.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
@@ -358,6 +359,16 @@ void GPUBuffer::SetData(const void* data, uint32 size)
|
||||
Log::ArgumentOutOfRangeException(TEXT("Buffer.SetData"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_desc.Usage == GPUResourceUsage::Default && GPUDevice::Instance->IsRendering())
|
||||
{
|
||||
// Upload using the context (will use internal staging buffer inside command buffer)
|
||||
RenderContext::GPULocker.Lock();
|
||||
GPUDevice::Instance->GetMainContext()->UpdateBuffer(this, data, size);
|
||||
RenderContext::GPULocker.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
void* mapped = Map(GPUResourceMapMode::Write);
|
||||
if (!mapped)
|
||||
return;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user