Compare commits
213 Commits
13bbaab1f3
...
work
| Author | SHA1 | Date | |
|---|---|---|---|
| c1f6bcb83f | |||
| eca4f6d9bc | |||
| 802074fca3 | |||
| 0bb13e1d91 | |||
| db47531a6d | |||
|
|
48c6339ebb | ||
|
|
2dd34b288c | ||
| 51feaa0730 | |||
| 8e69aa8bd6 | |||
| 90b2fded48 | |||
| 9125ffeb9e | |||
| 28980e5fbf | |||
| aab0d772a4 | |||
|
|
bf345f13ce | ||
|
|
a138c6b062 | ||
|
|
33e58c12cb | ||
|
|
094a6562b8 | ||
|
|
448eb48c23 | ||
|
|
78d519cb9a | ||
|
|
43d11264f8 | ||
|
|
f126a83b79 | ||
|
|
bdd7bae459 | ||
|
|
3dc7546dd4 | ||
|
|
185151b025 | ||
|
|
8cdec15fa6 | ||
|
|
1b40775d62 | ||
|
|
45e82d21f4 | ||
|
|
5c37584eca | ||
|
|
674fda7375 | ||
| 6296e1a9eb | |||
| 1586bb0702 | |||
|
|
d1c43ec1fe | ||
| edeaf6af09 | |||
| 951edd95db | |||
| 9951211596 | |||
| 6d337464f7 | |||
| d0b552d74a | |||
| 83512822b1 | |||
| ccb78103ec | |||
| 6e79ffea34 | |||
| 4f03d37a17 | |||
|
|
ef5d45874a | ||
|
|
d7df403e5e | ||
|
|
d3a50cdacb | ||
|
|
2e10d776e9 | ||
|
|
4ac870f701 | ||
|
|
6144f6c74e | ||
|
|
edb6884942 | ||
|
|
62e329ac6e | ||
|
|
68dce7e4dd | ||
| 6fb8419b3c | |||
| a4272d6ca9 | |||
|
|
bd2add7edd | ||
|
|
986693757c | ||
| 4654117d5e | |||
| 6ff260d052 | |||
| 88d2b72822 | |||
|
|
766091045b | ||
|
|
e2d9452994 | ||
|
|
eadb4411ff | ||
|
|
bdc87c7bc6 | ||
|
|
7606c9ac12 | ||
|
|
4240646ec7 | ||
|
|
0fa53f860a | ||
|
|
8ec138399a | ||
|
|
5b6859a66f | ||
|
|
e9835766bc | ||
|
|
d6eb647d59 | ||
|
|
b50f3fcb64 | ||
|
|
d6b4992991 | ||
|
|
cfd2f42b0c | ||
|
|
89c7f4b0a3 | ||
|
|
d7ff9fdade | ||
|
|
057ec9d41e | ||
|
|
7fa4efcac5 | ||
|
|
6547e7ee9c | ||
|
|
907c593671 | ||
| 29868531ad | |||
| 95dfb6fdc6 | |||
|
|
65ab42158d | ||
|
|
99841e2e8d | ||
|
|
73c30d3d89 | ||
|
|
bffb175a9b | ||
|
|
125a973ff2 | ||
|
|
462f75abd0 | ||
|
|
d95cd2f0be | ||
|
|
091f76bbf2 | ||
|
|
e8b60060ab | ||
|
|
cd637e8a7a | ||
|
|
9d8e75caa3 | ||
|
|
0670c0bbd3 | ||
|
|
f462a2187f | ||
| 8df3999f85 | |||
| 149b189629 | |||
| b1fd86e6b5 | |||
| 5e8fdf879d | |||
|
|
8eff098850 | ||
|
|
4fe9fdded6 | ||
|
|
03d52d4eb9 | ||
|
|
ab61ed5a37 | ||
|
|
72ee80242d | ||
|
|
9dc4dbc6d7 | ||
|
|
a74c5e7943 | ||
|
|
8f9fa6995e | ||
|
|
98e59450f1 | ||
|
|
8c62f1120f | ||
|
|
9aaba955d0 | ||
|
|
410ec0465c | ||
|
|
f9cb4ddae2 | ||
|
|
bb855e2663 | ||
|
|
d24f9d1e1e | ||
|
|
c1b1f4afc4 | ||
|
|
c639a3103c | ||
|
|
32bc73610f | ||
|
|
66dcfafa2e | ||
|
|
9215f2662f | ||
|
|
2dc404cbd3 | ||
| 84ada18299 | |||
| 635fe8017e | |||
| 834380ff05 | |||
| dd65fc2289 | |||
| 86125bb625 | |||
| 09a9d8b380 | |||
| 86b223ec93 | |||
| 78f6080321 | |||
| c7be6f6e0e | |||
| c40c31fbb7 | |||
| f5fbc1e32d | |||
| 0a20378acd | |||
| d4e87877b6 | |||
| 7eb7236b8a | |||
| dc0e4ffce2 | |||
| 86fe93943d | |||
| 3258113ef8 | |||
| ebf3999cfe | |||
| a1ccbbb5b9 | |||
| 35072c40d8 | |||
| 5c51021388 | |||
| 95fd527515 | |||
| 113d851cea | |||
| 72f0c460f9 | |||
| 13ddd15b44 | |||
| c9e24aaf5f | |||
| 68c0ac0ffc | |||
| bebda275a9 | |||
| 287eaae850 | |||
| 7e59c3b9a7 | |||
| 38658a5b8c | |||
| 88c75b8672 | |||
| 17ab1e6830 | |||
| 1f45110e5f | |||
| 5401166a07 | |||
| bfa8188782 | |||
| e777a71784 | |||
| c13e91a0d0 | |||
| aac5d57352 | |||
| ceca13c7b6 | |||
| 2905470330 | |||
| 2c17033822 | |||
| 0a4cb9e9b1 | |||
| 096651f4c1 | |||
| 275a08506b | |||
| 4cd61cb381 | |||
| 9ad1147581 | |||
| 35e09def1b | |||
| 0469607a71 | |||
| d115d22ee6 | |||
| 8120ae621d | |||
| f873c2fa9f | |||
| c3ffbe2f42 | |||
| 55a0a39881 | |||
| 592215dd30 | |||
| ef0c2a2785 | |||
| 37979e452a | |||
| e9671bb727 | |||
| aa328bd591 | |||
| 188e4313f9 | |||
| df02c70e31 | |||
| 257f54b323 | |||
| fb4b5b2575 | |||
| 4e1251276d | |||
| a7b200dc57 | |||
| 8cadbae80e | |||
| c3bae49aae | |||
|
|
6fa4fc6149 | ||
| f1ffe1acaa | |||
| 5c9ddf7f00 | |||
| 431767a150 | |||
| 8ba4e442a6 | |||
| 2f8d19c267 | |||
| f2fd98f742 | |||
| 3ce35d6e81 | |||
| 796dbaa836 | |||
| 84b209ec7f | |||
| fa976b34dc | |||
| e7cda362b7 | |||
| a41a09c162 | |||
| e82f84f0ab | |||
| f1d387ceea | |||
| 3893d4d1f8 | |||
| cf7ac50faf | |||
| 3f6bf15554 | |||
| dac74829b4 | |||
| 94d6f213a0 | |||
| 8f2550ef61 | |||
| b622a1cc5e | |||
| c83a3c32c7 | |||
| e7dcf7f7e7 | |||
| 398785a2be | |||
| 05dba0f1f5 | |||
| 39dbd32b2e | |||
| 23a34a455a | |||
| 4308328b48 |
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
|
||||
|
||||
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
|
||||
|
||||
@@ -28,6 +28,13 @@ TextureCube SkyLightTexture : register(t__SRV__);
|
||||
Buffer<float4> ShadowsBuffer : register(t__SRV__);
|
||||
Texture2D<float> ShadowMap : register(t__SRV__);
|
||||
@4// Forward Shading: Utilities
|
||||
// Public accessors for lighting data, use them as data binding might change but those methods will remain.
|
||||
LightData GetDirectionalLight() { return DirectionalLight; }
|
||||
LightData GetSkyLight() { return SkyLight; }
|
||||
ProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
|
||||
ExponentialHeightFogData GetExponentialHeightFog() { return ExponentialHeightFog; }
|
||||
uint GetLocalLightsCount() { return LocalLightsCount; }
|
||||
LightData GetLocalLight(uint i) { return LocalLights[i]; }
|
||||
@5// Forward Shading: Shaders
|
||||
|
||||
// Pixel Shader function for Forward Pass
|
||||
@@ -76,9 +83,8 @@ void PS_Forward(
|
||||
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
|
||||
|
||||
// Calculate lighting from a single directional light
|
||||
float4 shadowMask = 1.0f;
|
||||
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
shadowMask = GetShadowMask(shadow);
|
||||
float4 shadowMask = GetShadowMask(shadow);
|
||||
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
|
||||
|
||||
// Calculate lighting from sky light
|
||||
|
||||
BIN
Content/Shaders/ProbesFilter.flax
(Stored with Git LFS)
BIN
Content/Shaders/ProbesFilter.flax
(Stored with Git LFS)
Binary file not shown.
@@ -2,9 +2,9 @@
|
||||
"Name": "Flax",
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 10,
|
||||
"Minor": 11,
|
||||
"Revision": 0,
|
||||
"Build": 6705
|
||||
"Build": 6800
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
|
||||
@@ -13,6 +13,7 @@
|
||||
"Configuration": {
|
||||
"UseCSharp": true,
|
||||
"UseLargeWorlds": false,
|
||||
"UseDotNet": true
|
||||
"UseDotNet": true,
|
||||
"UseSDL": true
|
||||
}
|
||||
}
|
||||
@@ -14,4 +14,4 @@ bash ./Development/Scripts/Linux/CallBuildTool.sh --genproject "$@"
|
||||
# Build bindings for all editor configurations
|
||||
echo Building C# bindings...
|
||||
# TODO: Detect the correct architecture here
|
||||
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor
|
||||
#Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor
|
||||
|
||||
@@ -57,9 +57,9 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Arch: `sudo pacman -S git git-lfs`
|
||||
* `git-lfs install`
|
||||
* Install the required packages:
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev zenity wayland-protocols libportal-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel zenity wayland-protocols-devel libportal`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib zenity wayland-protocols libportal`
|
||||
* Install Clang compiler (version 6 or later):
|
||||
* Ubuntu: `sudo apt-get install clang lldb lld`
|
||||
* Fedora: `sudo dnf install clang llvm lldb lld`
|
||||
|
||||
@@ -174,7 +174,9 @@ void EditorAnalytics::StartSession()
|
||||
// Bind events
|
||||
GameCooker::OnEvent.Bind<RegisterGameCookingStart>();
|
||||
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Bind<RegisterLightmapsBuildingStart>();
|
||||
#if LOG_ENABLE
|
||||
Log::Logger::OnError.Bind<RegisterError>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditorAnalytics::EndSession()
|
||||
@@ -187,7 +189,9 @@ void EditorAnalytics::EndSession()
|
||||
// Unbind events
|
||||
GameCooker::OnEvent.Unbind<RegisterGameCookingStart>();
|
||||
ShadowsOfMordor::Builder::Instance()->OnBuildStarted.Unbind<RegisterLightmapsBuildingStart>();
|
||||
#if LOG_ENABLE
|
||||
Log::Logger::OnError.Unbind<RegisterError>();
|
||||
#endif
|
||||
|
||||
// End session
|
||||
{
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace FlaxEditor.Content
|
||||
|
||||
if (data is DragDataFiles)
|
||||
return DragDropEffect.Copy;
|
||||
return _dragOverItems.Effect;
|
||||
return _dragOverItems?.Effect ?? DragDropEffect.None;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeDescription => Path.EndsWith(".h") ? "C++ Header File" : "C++ Source Code";
|
||||
public override string TypeDescription => Path.EndsWith(".h") || Path.EndsWith(".hpp") ? "C++ Header File" : "C++ Source Code";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#if PLATFORM_TOOLS_WINDOWS
|
||||
#include "Platform/Windows/WindowsPlatformTools.h"
|
||||
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
|
||||
@@ -380,6 +381,7 @@ bool GameCooker::IsCancelRequested()
|
||||
|
||||
PlatformTools* GameCooker::GetTools(BuildPlatform platform)
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
PlatformTools* result = nullptr;
|
||||
if (!Tools.TryGet(platform, result))
|
||||
{
|
||||
@@ -471,6 +473,7 @@ bool GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
|
||||
LOG(Error, "Build platform {0} is not supported.", ::ToString(platform));
|
||||
return true;
|
||||
}
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Setup
|
||||
CancelFlag = 0;
|
||||
@@ -624,6 +627,7 @@ void GameCookerImpl::ReportProgress(const String& info, float totalProgress)
|
||||
|
||||
void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
if (Internal_OnCollectAssets == nullptr)
|
||||
{
|
||||
auto c = GameCooker::GetStaticClass();
|
||||
@@ -651,6 +655,7 @@ void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
|
||||
|
||||
bool GameCookerImpl::Build()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
CookingData& data = *Data;
|
||||
LOG(Info, "Starting Game Cooker...");
|
||||
LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration));
|
||||
@@ -778,6 +783,8 @@ int32 GameCookerImpl::ThreadFunction()
|
||||
|
||||
bool GameCookerService::Init()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
|
||||
editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading);
|
||||
GameCooker::OnCollectAssets.Bind(OnCollectAssets);
|
||||
@@ -789,6 +796,7 @@ void GameCookerService::Update()
|
||||
{
|
||||
if (IsRunning)
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
ScopeLock lock(ProgressLocker);
|
||||
|
||||
if (ProgressMsg.HasChars())
|
||||
|
||||
@@ -186,7 +186,7 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
|
||||
ADD_ENTRY("CFBundlePackageType", "APPL");
|
||||
ADD_ENTRY("NSPrincipalClass", "NSApplication");
|
||||
ADD_ENTRY("LSApplicationCategoryType", "public.app-category.games");
|
||||
ADD_ENTRY("LSMinimumSystemVersion", "10.15");
|
||||
ADD_ENTRY("LSMinimumSystemVersion", "13");
|
||||
ADD_ENTRY("CFBundleIconFile", "icon.icns");
|
||||
ADD_ENTRY_STR("CFBundleExecutable", executableName);
|
||||
ADD_ENTRY_STR("CFBundleIdentifier", appIdentifier);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "Engine/Engine/Base/GameBase.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Scripting/Enums.h"
|
||||
#if PLATFORM_TOOLS_WINDOWS
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Core/Types/Stopwatch.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/BinaryModule.h"
|
||||
@@ -69,6 +71,7 @@ MTypeObject* CustomEditorsUtil::GetCustomEditor(MTypeObject* refType)
|
||||
|
||||
bool CustomEditorsUtilService::Init()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly);
|
||||
Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded);
|
||||
|
||||
@@ -77,6 +80,8 @@ bool CustomEditorsUtilService::Init()
|
||||
|
||||
void OnAssemblyLoaded(MAssembly* assembly)
|
||||
{
|
||||
PROFILE_CPU_NAMED("CustomEditors.OnAssemblyLoaded");
|
||||
PROFILE_MEM(Editor);
|
||||
Stopwatch stopwatch;
|
||||
|
||||
// Prepare FlaxEngine
|
||||
|
||||
@@ -190,12 +190,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
foreach (var file in files)
|
||||
FindNewKeysCSharp(file, newKeys, allKeys);
|
||||
|
||||
// C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories);
|
||||
// C/C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.c", SearchOption.AllDirectories)).ToArray();
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories);
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.hpp", SearchOption.AllDirectories)).ToArray();;
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/ShadowsOfMordor/Builder.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#if PLATFORM_LINUX
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
@@ -47,6 +48,7 @@ void Editor::CloseSplashScreen()
|
||||
|
||||
bool Editor::CheckProjectUpgrade()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
const auto versionFilePath = Globals::ProjectCacheFolder / TEXT("version");
|
||||
|
||||
// Load version cache file
|
||||
@@ -366,6 +368,8 @@ bool Editor::BackupProject()
|
||||
|
||||
int32 Editor::LoadProduct()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Flax Editor product
|
||||
Globals::ProductName = TEXT("Flax Editor");
|
||||
Globals::CompanyName = TEXT("Flax");
|
||||
@@ -626,6 +630,7 @@ int32 Editor::LoadProduct()
|
||||
|
||||
Window* Editor::CreateMainWindow()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
Window* window = Managed->GetMainWindow();
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
@@ -662,6 +667,7 @@ bool Editor::Init()
|
||||
return true;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// If during last lightmaps baking engine crashed we could try to restore the progress
|
||||
ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState();
|
||||
@@ -693,11 +699,13 @@ bool Editor::Init()
|
||||
|
||||
void Editor::BeforeRun()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
Managed->BeforeRun();
|
||||
}
|
||||
|
||||
void Editor::BeforeExit()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
CloseSplashScreen();
|
||||
|
||||
Managed->Exit();
|
||||
@@ -708,6 +716,8 @@ void Editor::BeforeExit()
|
||||
|
||||
void EditorImpl::OnUpdate()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Update c# editor
|
||||
Editor::Managed->Update();
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// The column title horizontal text alignment
|
||||
/// </summary>
|
||||
public TextAlignment TitleAlignment = TextAlignment.Near;
|
||||
public TextAlignment TitleAlignment = TextAlignment.Center;
|
||||
|
||||
/// <summary>
|
||||
/// The column title margin.
|
||||
|
||||
@@ -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) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
@@ -121,7 +124,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>
|
||||
@@ -256,7 +259,9 @@ 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.Title = "ContextMenu";
|
||||
desc.HasSizingFrame = false;
|
||||
OnWindowCreating(ref desc);
|
||||
_window = Platform.CreateWindow(ref desc);
|
||||
@@ -265,6 +270,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
_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;
|
||||
@@ -440,6 +451,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;
|
||||
@@ -453,6 +475,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
@@ -551,7 +574,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Let root context menu to check if none of the popup windows
|
||||
if (_parentCM == null && UseVisibilityControl && !IsForeground)
|
||||
{
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
if (!IsMouseOver)
|
||||
Hide();
|
||||
#else
|
||||
Hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -326,8 +326,11 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
// Update eye dropper tool
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
// Try reading the color under the cursor in realtime if supported by the platform
|
||||
Float2 mousePosition = Platform.MousePosition;
|
||||
SelectedColor = ScreenUtilities.GetColorAt(mousePosition);
|
||||
Color color = ScreenUtilities.GetColorAt(mousePosition);
|
||||
if (color != Color.Transparent)
|
||||
SelectedColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,545 +0,0 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class DockHintWindow
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Float2 _defaultWindowSize;
|
||||
private Rectangle _rectDock;
|
||||
private Rectangle _rectWindow;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private bool _lateDragOffsetUpdate;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
|
||||
private DockHintWindow(FloatWindowDockPanel toMove)
|
||||
{
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Remove focus from drag target
|
||||
_toMove.Focus();
|
||||
_toMove.Defocus();
|
||||
|
||||
// Focus window
|
||||
window.Focus();
|
||||
|
||||
// Check if window is maximized and restore window.
|
||||
if (window.IsMaximized)
|
||||
{
|
||||
// Restore window and set position to mouse.
|
||||
var mousePos = window.MousePosition;
|
||||
var previousSize = window.Size;
|
||||
window.Restore();
|
||||
window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
|
||||
}
|
||||
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
var mouseScreenPosition = Platform.MousePosition;
|
||||
|
||||
// If the _toMove window was not focused when initializing this window, the result vector only contains zeros
|
||||
// and to prevent a failure, we need to perform an update for the drag offset at later time which will be done in the OnMouseMove event handler.
|
||||
if (mouseScreenPosition != Float2.Zero)
|
||||
CalculateDragOffset(mouseScreenPosition);
|
||||
else
|
||||
_lateDragOffsetUpdate = true;
|
||||
|
||||
// Get initial size
|
||||
_defaultWindowSize = window.Size;
|
||||
|
||||
// Init proxy window
|
||||
Proxy.Init(ref _defaultWindowSize);
|
||||
|
||||
// Bind events
|
||||
Proxy.Window.MouseUp += OnMouseUp;
|
||||
Proxy.Window.MouseMove += OnMouseMove;
|
||||
Proxy.Window.LostFocus += OnLostFocus;
|
||||
|
||||
// Start tracking mouse
|
||||
Proxy.Window.StartTrackingMouse(false);
|
||||
|
||||
// Update window GUI
|
||||
Proxy.Window.GUI.PerformLayout();
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects();
|
||||
|
||||
// Hide base window
|
||||
window.Hide();
|
||||
|
||||
// Enable hit window presentation
|
||||
Proxy.Window.RenderingEnabled = true;
|
||||
Proxy.Window.Show();
|
||||
Proxy.Window.Focus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// End tracking mouse
|
||||
Proxy.Window.EndTrackingMouse();
|
||||
|
||||
// Disable rendering
|
||||
Proxy.Window.RenderingEnabled = false;
|
||||
|
||||
// Unbind events
|
||||
Proxy.Window.MouseUp -= OnMouseUp;
|
||||
Proxy.Window.MouseMove -= OnMouseMove;
|
||||
Proxy.Window.LostFocus -= OnLostFocus;
|
||||
|
||||
// Hide the proxy
|
||||
Proxy.Hide();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
var window = _toMove.Window?.Window;
|
||||
if (window == null)
|
||||
return;
|
||||
var mouse = Platform.MousePosition;
|
||||
|
||||
// Move base window
|
||||
window.Position = mouse - _dragOffset;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new dragging hit window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <returns>The dock hint window object.</returns>
|
||||
public static DockHintWindow Create(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new DockHintWindow(toMove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the new dragging hit window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <returns>The dock hint window object.</returns>
|
||||
public static DockHintWindow Create(DockWindow toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Show floating
|
||||
toMove.ShowFloating();
|
||||
|
||||
// Move window to the mouse position (with some offset for caption bar)
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var mouse = Platform.MousePosition;
|
||||
window.Window.Position = mouse - new Float2(8, 8);
|
||||
|
||||
// Get floating panel
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new DockHintWindow(floatingPanelToMove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates window rectangle in the dock window.
|
||||
/// </summary>
|
||||
/// <param name="state">Window dock state.</param>
|
||||
/// <param name="rect">Dock panel rectangle.</param>
|
||||
/// <returns>Calculated window rectangle.</returns>
|
||||
public static Rectangle CalculateDockRect(DockState state, ref Rectangle rect)
|
||||
{
|
||||
Rectangle result = rect;
|
||||
switch (state)
|
||||
{
|
||||
case DockState.DockFill:
|
||||
result.Location.Y += DockPanel.DefaultHeaderHeight;
|
||||
result.Size.Y -= DockPanel.DefaultHeaderHeight;
|
||||
break;
|
||||
case DockState.DockTop:
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockLeft:
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockBottom:
|
||||
result.Location.Y += result.Size.Y * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.Y *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
case DockState.DockRight:
|
||||
result.Location.X += result.Size.X * (1 - DockPanel.DefaultSplitterValue);
|
||||
result.Size.X *= DockPanel.DefaultSplitterValue;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CalculateDragOffset(Float2 mouseScreenPosition)
|
||||
{
|
||||
var baseWinPos = _toMove.Window.Window.Position;
|
||||
_dragOffset = mouseScreenPosition - baseWinPos;
|
||||
}
|
||||
|
||||
private void UpdateRects()
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = Platform.MousePosition;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
var uiMouse = _mouse;
|
||||
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size;
|
||||
var offset = _rectDock.Location;
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_mouse))
|
||||
toSet = DockState.DockTop;
|
||||
else if (_rBottom.Contains(_mouse))
|
||||
toSet = DockState.DockBottom;
|
||||
else if (_rLeft.Contains(_mouse))
|
||||
toSet = DockState.DockLeft;
|
||||
else if (_rRight.Contains(_mouse))
|
||||
toSet = DockState.DockRight;
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_mouse))
|
||||
toSet = DockState.DockFill;
|
||||
_toSet = toSet;
|
||||
|
||||
// Show proxy hint windows
|
||||
Proxy.Down.Position = _rBottom.Location;
|
||||
Proxy.Left.Position = _rLeft.Location;
|
||||
Proxy.Right.Position = _rRight.Location;
|
||||
Proxy.Up.Position = _rUpper.Location;
|
||||
Proxy.Center.Position = _rCenter.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update proxy hint windows visibility
|
||||
Proxy.Down.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Left.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Right.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Up.IsVisible = showProxyHints & showBorderHints;
|
||||
Proxy.Center.IsVisible = showProxyHints & showCenterHint;
|
||||
|
||||
// Calculate proxy/dock/window rectangles
|
||||
if (_toDock == null)
|
||||
{
|
||||
// Floating window over nothing
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
// Floating window over dock panel
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use only part of the dock panel to show hint
|
||||
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
|
||||
}
|
||||
}
|
||||
|
||||
// Update proxy window
|
||||
Proxy.Window.ClientBounds = _rectWindow;
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(ref Float2 mousePos)
|
||||
{
|
||||
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
|
||||
if (_lateDragOffsetUpdate)
|
||||
{
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
CalculateDragOffset(mousePos);
|
||||
|
||||
// Reset state
|
||||
_lateDragOffsetUpdate = false;
|
||||
}
|
||||
|
||||
UpdateRects();
|
||||
}
|
||||
|
||||
private void OnLostFocus()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.
|
||||
/// </summary>
|
||||
public static class Proxy
|
||||
{
|
||||
/// <summary>
|
||||
/// The drag proxy window.
|
||||
/// </summary>
|
||||
public static Window Window;
|
||||
|
||||
/// <summary>
|
||||
/// The left hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Left;
|
||||
|
||||
/// <summary>
|
||||
/// The right hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Right;
|
||||
|
||||
/// <summary>
|
||||
/// The up hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Up;
|
||||
|
||||
/// <summary>
|
||||
/// The down hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Down;
|
||||
|
||||
/// <summary>
|
||||
/// The center hint proxy window.
|
||||
/// </summary>
|
||||
public static Window Center;
|
||||
|
||||
/// <summary>
|
||||
/// The hint windows size.
|
||||
/// </summary>
|
||||
public const float HintWindowsSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hit proxy windows. Those windows are used to indicate drag target areas (left, right, top, bottom, etc.).
|
||||
/// </summary>
|
||||
public static void InitHitProxy()
|
||||
{
|
||||
CreateProxy(ref Left, "DockHint.Left");
|
||||
CreateProxy(ref Right, "DockHint.Right");
|
||||
CreateProxy(ref Up, "DockHint.Up");
|
||||
CreateProxy(ref Down, "DockHint.Down");
|
||||
CreateProxy(ref Center, "DockHint.Center");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the hint window.
|
||||
/// </summary>
|
||||
/// <param name="initSize">Initial size of the proxy window.</param>
|
||||
public static void Init(ref Float2 initSize)
|
||||
{
|
||||
if (Window == null)
|
||||
{
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "DockHint.Window";
|
||||
settings.Size = initSize;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.IsTopmost = true;
|
||||
|
||||
Window = Platform.CreateWindow(ref settings);
|
||||
Window.Opacity = 0.6f;
|
||||
Window.GUI.BackgroundColor = Style.Current.Selection;
|
||||
Window.GUI.AddChild<DragVisuals>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resize proxy
|
||||
Window.ClientSize = initSize;
|
||||
}
|
||||
|
||||
InitHitProxy();
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateProxy(ref Window win, string name)
|
||||
{
|
||||
if (win != null)
|
||||
return;
|
||||
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = name;
|
||||
settings.Size = new Float2(HintWindowsSize * Platform.DpiScale);
|
||||
settings.AllowInput = false;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
settings.HasBorder = false;
|
||||
settings.HasSizingFrame = false;
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ActivateWhenFirstShown = false;
|
||||
settings.IsTopmost = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
|
||||
win = Platform.CreateWindow(ref settings);
|
||||
win.Opacity = 0.6f;
|
||||
win.GUI.BackgroundColor = Style.Current.Selection;
|
||||
win.GUI.AddChild<DragVisuals>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides proxy windows.
|
||||
/// </summary>
|
||||
public static void Hide()
|
||||
{
|
||||
HideProxy(ref Window);
|
||||
HideProxy(ref Left);
|
||||
HideProxy(ref Right);
|
||||
HideProxy(ref Up);
|
||||
HideProxy(ref Down);
|
||||
HideProxy(ref Center);
|
||||
}
|
||||
|
||||
private static void HideProxy(ref Window win)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
win.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases proxy data and windows.
|
||||
/// </summary>
|
||||
public static void Dispose()
|
||||
{
|
||||
DisposeProxy(ref Window);
|
||||
DisposeProxy(ref Left);
|
||||
DisposeProxy(ref Right);
|
||||
DisposeProxy(ref Up);
|
||||
DisposeProxy(ref Down);
|
||||
DisposeProxy(ref Center);
|
||||
}
|
||||
|
||||
private static void DisposeProxy(ref Window win)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
win.Close(ClosingReason.User);
|
||||
win = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MousePosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MouseStartPosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The start drag asynchronous window.
|
||||
@@ -165,7 +170,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating)
|
||||
{
|
||||
// Create docking hint window but in an async manner
|
||||
DockHintWindow.Create(_panel as FloatWindowDockPanel);
|
||||
WindowDragHelper.StartDragging(_panel as FloatWindowDockPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -176,7 +181,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
_panel.SelectTab(index - 1);
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(win);
|
||||
WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,6 +360,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (IsSingleFloatingWindow)
|
||||
return base.OnMouseDown(location, button);
|
||||
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
|
||||
MouseStartPosition = location;
|
||||
|
||||
// Check buttons
|
||||
if (button == MouseButton.Left)
|
||||
@@ -441,6 +447,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
// Check if single tab is tried to be moved
|
||||
else if (MouseDownWindow != null && _panel.TabsCount <= 1)
|
||||
{
|
||||
if ((MousePosition - MouseStartPosition).Length > 3)
|
||||
{
|
||||
// Clear flag
|
||||
IsMouseLeftButtonDown = false;
|
||||
|
||||
// Check tab under the mouse
|
||||
if (!IsMouseDownOverCross && MouseDownWindow != null)
|
||||
StartDrag(MouseDownWindow);
|
||||
MouseDownWindow = null;
|
||||
}
|
||||
}
|
||||
// Check if has more than one tab to change order
|
||||
else if (MouseDownWindow != null && _panel.TabsCount > 1)
|
||||
{
|
||||
|
||||
@@ -182,6 +182,25 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent)
|
||||
{
|
||||
CreateFloating(location, size, position, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
public void CreateFloating()
|
||||
{
|
||||
CreateFloating(Float2.Zero, Float2.Zero);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates the window in a floating state.
|
||||
/// </summary>
|
||||
/// <param name="location">Window location.</param>
|
||||
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
|
||||
/// <param name="position">Window location.</param>
|
||||
/// <param name="showWindow">Window visibility.</param>
|
||||
public void CreateFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent, bool showWindow = false)
|
||||
{
|
||||
Undock();
|
||||
|
||||
@@ -199,14 +218,17 @@ namespace FlaxEditor.GUI.Docking
|
||||
windowGUI.UnlockChildrenRecursive();
|
||||
windowGUI.PerformLayout();
|
||||
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
if (showWindow)
|
||||
{
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,42 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <seealso cref="DockPanel" />
|
||||
public class FloatWindowDockPanel : DockPanel
|
||||
{
|
||||
private class FloatWindowDecorations : WindowDecorations
|
||||
{
|
||||
private FloatWindowDockPanel _panel;
|
||||
|
||||
public FloatWindowDecorations(FloatWindowDockPanel panel)
|
||||
: base(panel.RootWindow)
|
||||
{
|
||||
_panel = panel;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_panel.BeginDrag();
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
protected override WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var hit = base.OnHitTest(ref mouse);
|
||||
if (hit == WindowHitCodes.Caption)
|
||||
{
|
||||
// Override the system behaviour when interacting with the caption area
|
||||
hit = WindowHitCodes.Client;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private MasterDockPanel _masterPanel;
|
||||
private WindowRootControl _window;
|
||||
|
||||
@@ -40,6 +76,26 @@ namespace FlaxEditor.GUI.Docking
|
||||
Parent = window;
|
||||
_window.Window.Closing += OnClosing;
|
||||
_window.Window.LeftButtonHit += OnLeftButtonHit;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
var decorations = Parent.AddChild(new FloatWindowDecorations(this));
|
||||
decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
var decorations = Parent.GetChild<FloatWindowDecorations>();
|
||||
if (decorations != null)
|
||||
{
|
||||
// Apply offset for the title bar
|
||||
foreach (var child in Children)
|
||||
child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +108,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
return;
|
||||
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(this);
|
||||
WindowDragHelper.StartDragging(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,22 +127,28 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.Title = title;
|
||||
settings.Size = size;
|
||||
settings.Position = location;
|
||||
settings.MinimumSize = new Float2(1);
|
||||
settings.MinimumSize = new Float2(200, 150);
|
||||
settings.MaximumSize = Float2.Zero; // Unlimited size
|
||||
settings.Fullscreen = false;
|
||||
settings.HasBorder = true;
|
||||
settings.SupportsTransparency = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ActivateWhenFirstShown = true;
|
||||
settings.AllowInput = true;
|
||||
settings.AllowMinimize = true;
|
||||
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;
|
||||
settings.StartPosition = startPosition;
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
//settings.HasSizingFrame = false;
|
||||
}
|
||||
|
||||
// Create window
|
||||
return Platform.CreateWindow(ref settings);
|
||||
|
||||
@@ -81,7 +81,6 @@ namespace FlaxEditor.GUI.Docking
|
||||
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
|
||||
{
|
||||
// Check all floating windows
|
||||
// TODO: gather windows order and take it into account when performing test
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
@@ -94,9 +93,44 @@ namespace FlaxEditor.GUI.Docking
|
||||
}
|
||||
|
||||
// Base
|
||||
//if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
// return null;
|
||||
return base.HitTest(ref position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs hit test over dock panel.
|
||||
/// </summary>
|
||||
/// <param name="position">Window space position to test.</param>
|
||||
/// <param name="excluded">Floating window to omit during searching (and all docked to that one).</param>
|
||||
/// <param name="hitResults">Results of the hit test</param>
|
||||
/// <returns>True if any dock panels were hit, otherwise false.</returns>
|
||||
public bool HitTest(ref Float2 position, FloatWindowDockPanel excluded, out DockPanel[] hitResults)
|
||||
{
|
||||
// Check all floating windows
|
||||
List<DockPanel> results = new(FloatingPanels.Count);
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
if (win.Visible && win != excluded)
|
||||
{
|
||||
var result = win.HitTest(ref position);
|
||||
if (result != null)
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
//if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
// return null;
|
||||
var baseResult = base.HitTest(ref position);
|
||||
if (baseResult != null)
|
||||
results.Add(baseResult);
|
||||
|
||||
hitResults = results.ToArray();
|
||||
return hitResults.Length > 0;
|
||||
}
|
||||
|
||||
internal void LinkWindow(DockWindow window)
|
||||
{
|
||||
// Add to the windows list
|
||||
|
||||
458
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
458
Source/Editor/GUI/Docking/WindowDragHelper.cs
Normal file
@@ -0,0 +1,458 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to handle docking windows dragging and docking.
|
||||
/// </summary>
|
||||
public class WindowDragHelper
|
||||
{
|
||||
private FloatWindowDockPanel _toMove;
|
||||
|
||||
private Float2 _dragOffset;
|
||||
private Rectangle _rectDock;
|
||||
private Float2 _mouse;
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private Window _dragSourceWindow;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
private Control _dockHintDown, _dockHintUp, _dockHintLeft, _dockHintRight, _dockHintCenter;
|
||||
|
||||
/// <summary>
|
||||
/// The hint control size.
|
||||
/// </summary>
|
||||
public const float HintControlSize = 32.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The opacity of the dragged window when hint controls are shown.
|
||||
/// </summary>
|
||||
public const float DragWindowOpacity = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if any windows are being dragged.
|
||||
/// </summary>
|
||||
public static bool IsDragActive { get; private set; }
|
||||
|
||||
private WindowDragHelper(FloatWindowDockPanel toMove, Window dragSourceWindow)
|
||||
{
|
||||
IsDragActive = true;
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Bind events
|
||||
FlaxEngine.Scripting.Update += OnUpdate;
|
||||
window.MouseUp += OnMouseUp;
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects(Platform.MousePosition);
|
||||
|
||||
// Ensure the dragged window stays on top of every other window
|
||||
window.IsAlwaysOnTop = true;
|
||||
|
||||
_dragSourceWindow = dragSourceWindow;
|
||||
if (_dragSourceWindow != null) // Detaching a tab from existing window
|
||||
{
|
||||
_dragOffset = new Float2(window.Size.X / 2, 10.0f);
|
||||
|
||||
_dragSourceWindow.MouseUp += OnMouseUp; // The mouse up event is sent to the source window on Windows
|
||||
|
||||
// TODO: when detaching tab in floating window (not main window), the drag source window is still main window?
|
||||
var dragSourceWindowWayland = toMove.MasterPanel?.RootWindow.Window ?? Editor.Instance.Windows.MainWindow;
|
||||
window.DoDragDrop(window.Title, _dragOffset, dragSourceWindowWayland);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dragOffset = window.MousePosition;
|
||||
window.DoDragDrop(window.Title, _dragOffset, window);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
IsDragActive = false;
|
||||
var window = _toMove?.Window?.Window;
|
||||
|
||||
// Unbind events
|
||||
FlaxEngine.Scripting.Update -= OnUpdate;
|
||||
if (window != null)
|
||||
window.MouseUp -= OnMouseUp;
|
||||
if (_dragSourceWindow != null)
|
||||
_dragSourceWindow.MouseUp -= OnMouseUp;
|
||||
|
||||
RemoveDockHints();
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
if (window != null)
|
||||
{
|
||||
window.Opacity = 1.0f;
|
||||
window.IsAlwaysOnTop = false;
|
||||
window.BringToFront();
|
||||
}
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
// Show base window
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
|
||||
|
||||
// Check if window has only single tab
|
||||
if (hasNoChildPanels && _toMove.TabsCount == 1)
|
||||
{
|
||||
// Dock window
|
||||
_toMove.GetTab(0).Show(_toSet, _toDock);
|
||||
}
|
||||
// Check if dock as tab and has no child panels
|
||||
else if (hasNoChildPanels && _toSet == DockState.DockFill)
|
||||
{
|
||||
// Dock all tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var selectedTab = _toMove.SelectedTab;
|
||||
|
||||
// Dock the first tab into the target location
|
||||
if (_toMove.TabsCount > 0)
|
||||
{
|
||||
var firstTab = _toMove.GetTab(0);
|
||||
firstTab.Show(_toSet, _toDock);
|
||||
|
||||
// Dock rest of the tabs
|
||||
while (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep selected tab being selected
|
||||
selectedTab?.SelectTab();
|
||||
}
|
||||
|
||||
// Focus target window
|
||||
_toDock.Root.Focus();
|
||||
}
|
||||
|
||||
_toMove = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a floating dock panel.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Floating dock panel to move.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(FloatWindowDockPanel toMove)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new WindowDragHelper(toMove, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start dragging a docked panel into a floating window.
|
||||
/// </summary>
|
||||
/// <param name="toMove">Dock window to move.</param>
|
||||
/// <param name="dragSourceWindow">The window where dragging started from.</param>
|
||||
/// <returns>The window drag helper object.</returns>
|
||||
public static WindowDragHelper StartDragging(DockWindow toMove, Window dragSourceWindow)
|
||||
{
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
// Create floating window
|
||||
toMove.CreateFloating();
|
||||
|
||||
// Get floating panel
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
|
||||
|
||||
return new WindowDragHelper(floatingPanelToMove, dragSourceWindow);
|
||||
}
|
||||
|
||||
private sealed class DragVisuals : Control
|
||||
{
|
||||
public DragVisuals()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.SelectionBorder);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp += OnMouseUp;
|
||||
|
||||
_dockHintDown = AddHintControl(new Float2(0.5f, 1));
|
||||
_dockHintUp = AddHintControl(new Float2(0.5f, 0));
|
||||
_dockHintLeft = AddHintControl(new Float2(0, 0.5f));
|
||||
_dockHintRight = AddHintControl(new Float2(1, 0.5f));
|
||||
_dockHintCenter = AddHintControl(new Float2(0.5f, 0.5f));
|
||||
|
||||
Control AddHintControl(Float2 pivot)
|
||||
{
|
||||
DragVisuals hintControl = _toDock.AddChild<DragVisuals>();
|
||||
hintControl.Size = new Float2(HintControlSize);
|
||||
hintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
hintControl.Pivot = pivot;
|
||||
hintControl.PivotRelative = true;
|
||||
return hintControl;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
if (_toDock.RootWindow.Window != _dragSourceWindow)
|
||||
_toDock.RootWindow.Window.MouseUp -= OnMouseUp;
|
||||
|
||||
_dockHintDown?.Parent.RemoveChild(_dockHintDown);
|
||||
_dockHintUp?.Parent.RemoveChild(_dockHintUp);
|
||||
_dockHintLeft?.Parent.RemoveChild(_dockHintLeft);
|
||||
_dockHintRight?.Parent.RemoveChild(_dockHintRight);
|
||||
_dockHintCenter?.Parent.RemoveChild(_dockHintCenter);
|
||||
_dockHintDown = _dockHintUp = _dockHintLeft = _dockHintRight = _dockHintCenter = null;
|
||||
}
|
||||
|
||||
private void UpdateRects(Float2 mousePos)
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = mousePos;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
DockPanel dockPanel = null;
|
||||
if (_toMove.MasterPanel.HitTest(ref _mouse, _toMove, out var hitResults))
|
||||
{
|
||||
dockPanel = hitResults[0];
|
||||
|
||||
// Prefer panel which currently has focus
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow.Window.IsFocused)
|
||||
{
|
||||
dockPanel = hit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer panel in the same window we hit earlier
|
||||
if (dockPanel?.RootWindow != _toDock?.RootWindow)
|
||||
{
|
||||
foreach (var hit in hitResults)
|
||||
{
|
||||
if (hit.RootWindow == _toDock?.RootWindow)
|
||||
{
|
||||
dockPanel = _toDock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dockPanel != _toDock)
|
||||
{
|
||||
RemoveDockHints();
|
||||
_toDock = dockPanel;
|
||||
AddDockHints();
|
||||
|
||||
// Make sure the all the dock hint areas are not under other windows
|
||||
_toDock?.RootWindow.Window.BringToFront();
|
||||
//_toDock?.RootWindow.Window.Focus();
|
||||
|
||||
// Make the dragged window transparent when dock hints are visible
|
||||
_toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity;
|
||||
}
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
Control hoveredHintControl = null;
|
||||
Float2 hoveredSizeOverride = Float2.Zero;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showCenterHint = false;
|
||||
|
||||
// Disable docking windows with one or more dock panels inside
|
||||
if (_toMove.ChildPanelsCount > 0)
|
||||
showBorderHints = false;
|
||||
|
||||
// Get dock area
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size / Platform.DpiScale;
|
||||
var offset = _toDock.PointFromScreen(_rectDock.Location);
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = HintControlSize;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var hintPreviewSize = new Float2(Math.Max(HintControlSize * 2, size.X * 0.5f), Math.Max(HintControlSize * 2, size.Y * 0.5f));
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test, and calculate the approximation for filled area when hovered over the hint
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockTop;
|
||||
hoveredHintControl = _dockHintUp;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockBottom;
|
||||
hoveredHintControl = _dockHintDown;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockLeft;
|
||||
hoveredHintControl = _dockHintLeft;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
else if (_rRight.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockRight;
|
||||
hoveredHintControl = _dockHintRight;
|
||||
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
|
||||
}
|
||||
}
|
||||
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockFill;
|
||||
hoveredHintControl = _dockHintCenter;
|
||||
hoveredSizeOverride = new Float2(size.X, size.Y);
|
||||
}
|
||||
|
||||
_toSet = toSet;
|
||||
}
|
||||
else
|
||||
{
|
||||
_toSet = DockState.Float;
|
||||
}
|
||||
|
||||
// Update sizes and opacity of hint controls
|
||||
if (_toDock != null)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
{
|
||||
_dockHintDown.Size = new Float2(HintControlSize);
|
||||
_dockHintDown.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
{
|
||||
_dockHintLeft.Size = new Float2(HintControlSize);
|
||||
_dockHintLeft.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
{
|
||||
_dockHintRight.Size = new Float2(HintControlSize);
|
||||
_dockHintRight.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
{
|
||||
_dockHintUp.Size = new Float2(HintControlSize);
|
||||
_dockHintUp.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
{
|
||||
_dockHintCenter.Size = new Float2(HintControlSize);
|
||||
_dockHintCenter.BackgroundColor = Style.Current.Selection.AlphaMultiplied(0.6f);
|
||||
}
|
||||
|
||||
if (_toSet != DockState.Float)
|
||||
{
|
||||
if (hoveredHintControl != null)
|
||||
{
|
||||
hoveredHintControl.BackgroundColor = Style.Current.Selection.AlphaMultiplied(1.0f);
|
||||
hoveredHintControl.Size = hoveredSizeOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update hint controls visibility and location
|
||||
if (showProxyHints)
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
_dockHintDown.Location = _rBottom.Location;
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
_dockHintLeft.Location = _rLeft.Location;
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
_dockHintRight.Location = _rRight.Location;
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
_dockHintUp.Location = _rUpper.Location;
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
_dockHintCenter.Location = _rCenter.Location;
|
||||
|
||||
_dockHintDown.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintLeft.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintRight.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintUp.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintCenter.Visible = showProxyHints & showCenterHint;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void OnUpdate()
|
||||
{
|
||||
var mousePos = Platform.MousePosition;
|
||||
|
||||
if (_mouse != mousePos)
|
||||
OnMouseMove(mousePos);
|
||||
}
|
||||
|
||||
private void OnMouseMove(Float2 mousePos)
|
||||
{
|
||||
if (_dragSourceWindow != null)
|
||||
_toMove.Window.Window.Position = mousePos - _dragOffset;
|
||||
|
||||
UpdateRects(mousePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
@@ -292,13 +293,45 @@ 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)
|
||||
{
|
||||
if (button == MouseButton.Left && _isSliding)
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
// End sliding and return mouse to original location
|
||||
RootWindow.MousePosition = _mouseClickedPosition;
|
||||
#endif
|
||||
EndSliding();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,16 +12,6 @@ namespace FlaxEditor.GUI
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public sealed class MainMenu : ContainerControl
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
private bool _useCustomWindowSystem;
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
#endif
|
||||
private MainMenuButton _selected;
|
||||
|
||||
/// <summary>
|
||||
@@ -60,200 +50,12 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MainMenu"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mainWindow">The main window.</param>
|
||||
public MainMenu(RootControl mainWindow)
|
||||
public MainMenu()
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
Height = 28;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_window = mainWindow.RootWindow.Window;
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(6, 6, 6, 6),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
KeepAspectRatio = false,
|
||||
TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration),
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Color.Transparent,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest = null;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
var dpiScale = _window.DpiScale;
|
||||
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var mousePos = PointFromScreen(mouse * dpiScale);
|
||||
var controlUnderMouse = GetChildAt(mousePos);
|
||||
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
|
||||
var rb = GetRightButton();
|
||||
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight, _minimizeButton.BottomLeft - rb.UpperRight).Contains(ref mousePos) && !isMouseOverSth)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Return the rightmost button.
|
||||
/// </summary>
|
||||
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
|
||||
private MainMenuButton GetRightButton()
|
||||
{
|
||||
MainMenuButton b = null;
|
||||
foreach (var control in Children)
|
||||
{
|
||||
if (b == null && control is MainMenuButton)
|
||||
b = (MainMenuButton)control;
|
||||
|
||||
if (control is MainMenuButton && control.Right > b.Right)
|
||||
b = (MainMenuButton)control;
|
||||
}
|
||||
return b;
|
||||
BackgroundColor = Style.Current.LightBackground;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,26 +100,6 @@ namespace FlaxEditor.GUI
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
@@ -333,16 +115,8 @@ namespace FlaxEditor.GUI
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
float x = 0;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Icon
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
#endif
|
||||
WindowDecorations decorations = Parent.GetChild<WindowDecorations>();
|
||||
x += decorations?.Icon?.Width ?? 0;
|
||||
|
||||
// Arrange controls
|
||||
MainMenuButton rightMostButton = null;
|
||||
@@ -361,37 +135,21 @@ namespace FlaxEditor.GUI
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (_useCustomWindowSystem)
|
||||
{
|
||||
// Buttons
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = Width - _closeButton.Width;
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = _closeButton.X - _maximizeButton.Width;
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = _maximizeButton.X - _minimizeButton.Width;
|
||||
|
||||
// Title
|
||||
_title.Bounds = new Rectangle(x + 2, 0, _minimizeButton.Left - x - 4, Height);
|
||||
//_title.Text = _title.Width < 300.0f ? Editor.Instance.ProjectInfo.Name : _window.Title;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill the right side if title and buttons are not present
|
||||
if (decorations?.Title == null)
|
||||
Width = Parent.Width;
|
||||
else
|
||||
Width = x;
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
|
||||
if (_selected != null)
|
||||
Selected = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,12 @@ namespace FlaxEditor.GUI
|
||||
Text = text;
|
||||
|
||||
var style = Style.Current;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
if (!Utilities.Utils.UseCustomWindowDecorations())
|
||||
{
|
||||
BackgroundColorMouseOver = style.BackgroundHighlighted;
|
||||
BackgroundColorMouseOverOpened = style.Background;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f;
|
||||
}
|
||||
|
||||
342
Source/Editor/GUI/WindowDecorations.cs
Normal file
342
Source/Editor/GUI/WindowDecorations.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the title bar of the window with buttons.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class WindowDecorations : ContainerControl
|
||||
{
|
||||
private Image _icon;
|
||||
private Label _title;
|
||||
private Button _closeButton;
|
||||
private Button _minimizeButton;
|
||||
private Button _maximizeButton;
|
||||
private LocalizedString _charChromeRestore, _charChromeMaximize;
|
||||
private Window _window;
|
||||
|
||||
/// <summary>
|
||||
/// The title label in the title bar.
|
||||
/// </summary>
|
||||
public Label Title => _title;
|
||||
|
||||
/// <summary>
|
||||
/// The icon used in the title bar.
|
||||
/// </summary>
|
||||
public Image Icon => _icon;
|
||||
|
||||
/// <summary>
|
||||
/// The tooltip shown when hovering over the icon.
|
||||
/// </summary>
|
||||
public string IconTooltipText
|
||||
{
|
||||
get => _icon?.TooltipText ?? null;
|
||||
set
|
||||
{
|
||||
if (_icon != null)
|
||||
_icon.TooltipText = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WindowDecorations"/> class.
|
||||
/// </summary>
|
||||
/// <param name="window">The window.</param>
|
||||
/// <param name="iconOnly">When set, omit drawing title and buttons.</param>
|
||||
public WindowDecorations(RootControl window, bool iconOnly = false)
|
||||
: base(0, 0, 0, 20)
|
||||
{
|
||||
_window = window.RootWindow.Window;
|
||||
|
||||
AutoFocus = false;
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
BackgroundColor = Color.Transparent;
|
||||
|
||||
var windowIcon = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.WindowIcon);
|
||||
|
||||
_icon = new Image
|
||||
{
|
||||
Margin = new Margin(4, 4, 4, 4),
|
||||
Brush = new TextureBrush(windowIcon),
|
||||
Color = Style.Current.Foreground,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
KeepAspectRatio = false,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
if (!iconOnly)
|
||||
{
|
||||
_icon.Margin = new Margin(6, 6, 6, 6);
|
||||
Height = 28;
|
||||
|
||||
_window.HitTest += OnHitTest;
|
||||
_window.Closed += OnWindowClosed;
|
||||
|
||||
FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.WindowIconsFont);
|
||||
Font iconFont = windowIconsFont?.CreateFont(9);
|
||||
|
||||
_title = new Label(0, 0, Width, Height)
|
||||
{
|
||||
Text = _window.Title,
|
||||
HorizontalAlignment = TextAlignment.Center,
|
||||
VerticalAlignment = TextAlignment.Center,
|
||||
ClipText = true,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
TextColorHighlighted = Style.Current.ForegroundGrey,
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
Parent = this,
|
||||
};
|
||||
|
||||
_closeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Color.Red,
|
||||
BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_closeButton.Clicked += () => _window.Close(ClosingReason.User);
|
||||
|
||||
_minimizeButton = new Button
|
||||
{
|
||||
Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_minimizeButton.Clicked += () => _window.Minimize();
|
||||
|
||||
_maximizeButton = new Button
|
||||
{
|
||||
Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(),
|
||||
Font = new FontReference(iconFont),
|
||||
BackgroundColor = Style.Current.LightBackground,
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = Color.Transparent,
|
||||
BorderColorSelected = Color.Transparent,
|
||||
TextColor = Style.Current.Foreground,
|
||||
Width = 46,
|
||||
BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f),
|
||||
Parent = this,
|
||||
};
|
||||
_maximizeButton.Clicked += () =>
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
};
|
||||
|
||||
_charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString();
|
||||
_charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
var maximizeText = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize;
|
||||
if (_maximizeButton.Text != maximizeText)
|
||||
_maximizeButton.Text = maximizeText;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowClosed()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
_window.HitTest -= OnHitTest;
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform hit test on the window.
|
||||
/// </summary>
|
||||
/// <param name="mouse">The mouse position</param>
|
||||
/// <returns>The hit code for given position.</returns>
|
||||
protected virtual WindowHitCodes OnHitTest(ref Float2 mouse)
|
||||
{
|
||||
if (_window.IsMinimized)
|
||||
return WindowHitCodes.NoWhere;
|
||||
|
||||
var dpiScale = _window.DpiScale;
|
||||
var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var winSize = _window.Size;
|
||||
|
||||
// Distance from which the mouse is considered to be on the border/corner
|
||||
float distance = 5.0f * dpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var controlUnderMouse = GetChildAt(pos, control => control != _title);
|
||||
if (_title.Bounds.Contains(pos) && controlUnderMouse == null)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
// These may not work with main window due to SDL not passing mouse events
|
||||
// when interacting with hit tests on caption area...
|
||||
|
||||
if (Title.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
if (_window.IsMaximized)
|
||||
_window.Restore();
|
||||
else
|
||||
_window.Maximize();
|
||||
return true;
|
||||
}
|
||||
else if (Icon.Bounds.Contains(location) && button == MouseButton.Left)
|
||||
{
|
||||
_window.Close(ClosingReason.User);
|
||||
return true;
|
||||
}
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutAfterChildren()
|
||||
{
|
||||
// Calculate extents for title bounds area excluding the icon and main menu area
|
||||
float x = 0;
|
||||
|
||||
// Icon
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.X = x;
|
||||
_icon.Size = new Float2(Height);
|
||||
x += _icon.Width;
|
||||
}
|
||||
|
||||
// Main menu if present
|
||||
if (Parent.GetChild<MainMenu>() is MainMenu mainMenu)
|
||||
{
|
||||
for (int i = 0; i < mainMenu.Children.Count; i++)
|
||||
{
|
||||
var c = mainMenu.Children[i];
|
||||
if (c is MainMenuButton b && c.Visible)
|
||||
{
|
||||
b.Bounds = new Rectangle(x, 0, b.Width, Height);
|
||||
x += b.Width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons
|
||||
float rightMostButtonX = Width;
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.Height = Height;
|
||||
_closeButton.X = rightMostButtonX - _closeButton.Width;
|
||||
rightMostButtonX = _closeButton.X;
|
||||
}
|
||||
if (_maximizeButton != null)
|
||||
{
|
||||
_maximizeButton.Height = Height;
|
||||
_maximizeButton.X = rightMostButtonX - _maximizeButton.Width;
|
||||
rightMostButtonX = _maximizeButton.X;
|
||||
}
|
||||
if (_minimizeButton != null)
|
||||
{
|
||||
_minimizeButton.Height = Height;
|
||||
_minimizeButton.X = rightMostButtonX - _minimizeButton.Width;
|
||||
rightMostButtonX = _minimizeButton.X;
|
||||
}
|
||||
|
||||
// Title
|
||||
if (_title != null)
|
||||
{
|
||||
_title.Text = _window.Title;
|
||||
_title.Bounds = new Rectangle(x, 0, rightMostButtonX - x, Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
DrawBorders();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw borders around the window.
|
||||
/// </summary>
|
||||
public virtual void DrawBorders()
|
||||
{
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
if (Editor.Instance.UI.StatusBar == null)
|
||||
return;
|
||||
|
||||
const float thickness = 1.0f;
|
||||
Color color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
Rectangle rect = new Rectangle(thickness * 0.5f, thickness * 0.5f, Parent.Width - thickness, Parent.Height - thickness);
|
||||
Render2D.DrawRectangle(rect, color);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_window != null)
|
||||
{
|
||||
_window.Closed -= OnWindowClosed;
|
||||
OnWindowClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,10 +402,11 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
for (const auto& e : inputEvents)
|
||||
{
|
||||
auto window = e.Target ? e.Target : defaultWindow;
|
||||
if (!window)
|
||||
if (!window || window->IsClosed())
|
||||
continue;
|
||||
switch (e.Type)
|
||||
{
|
||||
@@ -435,12 +436,14 @@ 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;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
}
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
Array<Window*, InlinedAllocation<32>> windows;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h"
|
||||
#include "Engine/Content/Assets/VisualScript.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/CSG/CSGBuilder.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Renderer/ProbesRenderer.h"
|
||||
@@ -74,7 +75,7 @@ void OnLightmapsBuildFinished(bool failed)
|
||||
OnLightmapsBake(ShadowsOfMordor::BuildProgressStep::GenerateLightmapCharts, 0, 0, false);
|
||||
}
|
||||
|
||||
void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
|
||||
void OnBakeEvent(bool started, Actor* e)
|
||||
{
|
||||
if (Internal_EnvProbeBake == nullptr)
|
||||
{
|
||||
@@ -82,7 +83,7 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
|
||||
ASSERT(Internal_EnvProbeBake);
|
||||
}
|
||||
|
||||
MObject* probeObj = e.Actor ? e.Actor->GetManagedInstance() : nullptr;
|
||||
MObject* probeObj = e ? e->GetManagedInstance() : nullptr;
|
||||
|
||||
MainThreadManagedInvokeAction::ParamsBuilder params;
|
||||
params.AddParam(started);
|
||||
@@ -90,12 +91,12 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
|
||||
MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params);
|
||||
}
|
||||
|
||||
void OnRegisterBake(const ProbesRenderer::Entry& e)
|
||||
void OnRegisterBake(Actor* e)
|
||||
{
|
||||
OnBakeEvent(true, e);
|
||||
}
|
||||
|
||||
void OnFinishBake(const ProbesRenderer::Entry& e)
|
||||
void OnFinishBake(Actor* e)
|
||||
{
|
||||
OnBakeEvent(false, e);
|
||||
}
|
||||
@@ -156,7 +157,9 @@ ManagedEditor::ManagedEditor()
|
||||
lightmapsBuilder->OnBuildProgress.Bind<OnLightmapsBuildProgress>();
|
||||
lightmapsBuilder->OnBuildFinished.Bind<OnLightmapsBuildFinished>();
|
||||
CSG::Builder::OnBrushModified.Bind<OnBrushModified>();
|
||||
#if LOG_ENABLE
|
||||
Log::Logger::OnMessage.Bind<OnLogMessage>();
|
||||
#endif
|
||||
VisualScripting::DebugFlow.Bind<OnVisualScriptingDebugFlow>();
|
||||
}
|
||||
|
||||
@@ -172,7 +175,9 @@ ManagedEditor::~ManagedEditor()
|
||||
lightmapsBuilder->OnBuildProgress.Unbind<OnLightmapsBuildProgress>();
|
||||
lightmapsBuilder->OnBuildFinished.Unbind<OnLightmapsBuildFinished>();
|
||||
CSG::Builder::OnBrushModified.Unbind<OnBrushModified>();
|
||||
#if LOG_ENABLE
|
||||
Log::Logger::OnMessage.Unbind<OnLogMessage>();
|
||||
#endif
|
||||
VisualScripting::DebugFlow.Unbind<OnVisualScriptingDebugFlow>();
|
||||
}
|
||||
|
||||
|
||||
@@ -1013,7 +1013,7 @@ namespace FlaxEditor.Modules
|
||||
ContentItem item;
|
||||
if (path.EndsWith(".cs"))
|
||||
item = new CSharpScriptItem(path);
|
||||
else if (path.EndsWith(".cpp") || path.EndsWith(".h"))
|
||||
else if (path.EndsWith(".cpp") || path.EndsWith(".h") || path.EndsWith(".c") || path.EndsWith(".hpp"))
|
||||
item = new CppScriptItem(path);
|
||||
else if (path.EndsWith(".shader") || path.EndsWith(".hlsl"))
|
||||
item = new ShaderSourceItem(path);
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace FlaxEditor.Modules
|
||||
outputExtension = extension;
|
||||
|
||||
// Check if can place source files here
|
||||
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h"))
|
||||
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h" || extension == ".c" || extension == ".hpp"))
|
||||
{
|
||||
// Error
|
||||
Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path));
|
||||
|
||||
@@ -711,7 +711,11 @@ namespace FlaxEditor.Modules
|
||||
|
||||
private void OnActorChildNodesDispose(ActorNode node)
|
||||
{
|
||||
if (Selection.Count == 0)
|
||||
return;
|
||||
|
||||
// TODO: cache if selection contains any actor child node and skip this loop if no need to iterate
|
||||
// TODO: or build a hash set with selected nodes for quick O(1) checks (cached until selection changes)
|
||||
|
||||
// Deselect child nodes
|
||||
for (int i = 0; i < node.ChildNodes.Count; i++)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEngine;
|
||||
@@ -658,6 +659,48 @@ namespace FlaxEditor.Modules
|
||||
//node?.TreeNode.OnActiveChanged();
|
||||
}
|
||||
|
||||
private void OnActorDestroyChildren(Actor actor)
|
||||
{
|
||||
// Instead of doing OnActorParentChanged for every child lets remove all of them at once from that actor
|
||||
ActorNode node = GetActorNode(actor);
|
||||
if (node != null)
|
||||
{
|
||||
if (Editor.SceneEditing.HasSthSelected)
|
||||
{
|
||||
// Clear selection if one of the removed actors is selected
|
||||
var selection = new HashSet<Actor>();
|
||||
foreach (var e in Editor.SceneEditing.Selection)
|
||||
{
|
||||
if (e is ActorNode q && q.Actor)
|
||||
selection.Add(q.Actor);
|
||||
}
|
||||
var count = actor.ChildrenCount;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var child = actor.GetChild(i);
|
||||
if (selection.Contains(child))
|
||||
{
|
||||
Editor.SceneEditing.Deselect();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all child nodes (upfront remove all nodes to run faster)
|
||||
for (int i = 0; i < node.ChildNodes.Count; i++)
|
||||
{
|
||||
if (node.ChildNodes[i] is ActorNode child)
|
||||
child.parentNode = null;
|
||||
}
|
||||
node.TreeNode.DisposeChildren();
|
||||
for (int i = 0; i < node.ChildNodes.Count; i++)
|
||||
{
|
||||
node.ChildNodes[i].Dispose();
|
||||
}
|
||||
node.ChildNodes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor node.
|
||||
/// </summary>
|
||||
@@ -709,6 +752,7 @@ namespace FlaxEditor.Modules
|
||||
Level.ActorOrderInParentChanged += OnActorOrderInParentChanged;
|
||||
Level.ActorNameChanged += OnActorNameChanged;
|
||||
Level.ActorActiveChanged += OnActorActiveChanged;
|
||||
Level.ActorDestroyChildren += OnActorDestroyChildren;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -726,6 +770,7 @@ namespace FlaxEditor.Modules
|
||||
Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged;
|
||||
Level.ActorNameChanged -= OnActorNameChanged;
|
||||
Level.ActorActiveChanged -= OnActorActiveChanged;
|
||||
Level.ActorDestroyChildren -= OnActorDestroyChildren;
|
||||
|
||||
// Cleanup graph
|
||||
Root.Dispose();
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnPlayBegin()
|
||||
public override void OnPlayBeginning()
|
||||
{
|
||||
Editor.Windows.FlashMainWindow();
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow;
|
||||
using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Options;
|
||||
@@ -29,6 +28,40 @@ namespace FlaxEditor.Modules
|
||||
/// <seealso cref="FlaxEditor.Modules.EditorModule" />
|
||||
public sealed class UIModule : EditorModule
|
||||
{
|
||||
private class MainWindowDecorations : WindowDecorations
|
||||
{
|
||||
public MainWindowDecorations(RootControl window, bool iconOnly)
|
||||
: base(window, iconOnly)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (base.OnKeyDown(key))
|
||||
return true;
|
||||
|
||||
// Fallback to the edit window for shortcuts
|
||||
var editor = Editor.Instance;
|
||||
return editor.Windows.EditWin.InputActions.Process(editor, this, key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawBorders()
|
||||
{
|
||||
// Draw main window borders if using a custom style
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
var color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
|
||||
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
|
||||
}
|
||||
}
|
||||
|
||||
private Label _progressLabel;
|
||||
private ProgressBar _progressBar;
|
||||
private Button _outputLogButton;
|
||||
@@ -97,6 +130,11 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public MainMenu MainMenu;
|
||||
|
||||
/// <summary>
|
||||
/// The window decorations (title bar with buttons)
|
||||
/// </summary>
|
||||
public WindowDecorations WindowDecorations;
|
||||
|
||||
/// <summary>
|
||||
/// The tool strip control.
|
||||
/// </summary>
|
||||
@@ -377,19 +415,11 @@ namespace FlaxEditor.Modules
|
||||
InitToolstrip(mainWindow);
|
||||
InitStatusBar(mainWindow);
|
||||
InitDockPanel(mainWindow);
|
||||
InitWindowDecorations(mainWindow);
|
||||
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
|
||||
// Add dummy control for drawing the main window borders if using a custom style
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
|
||||
#endif
|
||||
{
|
||||
mainWindow.AddChild(new CustomWindowBorderControl
|
||||
{
|
||||
Size = Float2.Zero,
|
||||
});
|
||||
}
|
||||
|
||||
mainWindow.PerformLayout(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -410,23 +440,6 @@ namespace FlaxEditor.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomWindowBorderControl : Control
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
var win = RootWindow.Window;
|
||||
if (win.IsMaximized)
|
||||
return;
|
||||
|
||||
var color = Editor.Instance.UI.StatusBar.StatusColor;
|
||||
var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color);
|
||||
Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color);
|
||||
Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndInit()
|
||||
{
|
||||
@@ -458,13 +471,6 @@ namespace FlaxEditor.Modules
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnExit()
|
||||
{
|
||||
// Cleanup dock panel hint proxy windows (Flax will destroy them by var but it's better to clear them earlier)
|
||||
DockHintWindow.Proxy.Dispose();
|
||||
}
|
||||
|
||||
private IColorPickerDialog ShowPickColorDialog(Control targetControl, Color initialValue, ColorValueBox.ColorPickerEvent colorChanged, ColorValueBox.ColorPickerClosedEvent pickerClosed, bool useDynamicEditing)
|
||||
{
|
||||
var dialog = new ColorPickerDialog(initialValue, colorChanged, pickerClosed, useDynamicEditing);
|
||||
@@ -518,10 +524,12 @@ namespace FlaxEditor.Modules
|
||||
|
||||
private void InitMainMenu(RootControl mainWindow)
|
||||
{
|
||||
MainMenu = new MainMenu(mainWindow)
|
||||
MainMenu = new MainMenu()
|
||||
{
|
||||
Parent = mainWindow
|
||||
};
|
||||
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
MainMenu.Height = 28;
|
||||
|
||||
var inputOptions = Editor.Options.Options.Input;
|
||||
|
||||
@@ -661,6 +669,20 @@ namespace FlaxEditor.Modules
|
||||
cm.AddButton("Information about Flax", () => new AboutDialog().Show());
|
||||
}
|
||||
|
||||
private void InitWindowDecorations(RootControl mainWindow)
|
||||
{
|
||||
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
|
||||
var driver = Platform.DisplayServer;
|
||||
if (!string.IsNullOrEmpty(driver))
|
||||
driver = $" ({driver})";
|
||||
|
||||
WindowDecorations = new MainWindowDecorations(mainWindow, !Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
{
|
||||
Parent = mainWindow,
|
||||
IconTooltipText = $"{mainWindow.RootWindow.Title}\nVersion {Globals.EngineVersion}\nConfiguration {configuration}\nGraphics {GPUDevice.Instance.RendererType}{driver}",
|
||||
};
|
||||
}
|
||||
|
||||
private void OnOptionsChanged(EditorOptions options)
|
||||
{
|
||||
var inputOptions = options.Input;
|
||||
@@ -1070,6 +1092,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
// Clear UI references (GUI cannot be used after window closing)
|
||||
MainMenu = null;
|
||||
WindowDecorations = null;
|
||||
ToolStrip = null;
|
||||
MasterPanel = null;
|
||||
StatusBar = null;
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.Text;
|
||||
using System.Xml;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEditor.Windows.Profiler;
|
||||
@@ -758,17 +757,19 @@ namespace FlaxEditor.Modules
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "Flax Editor";
|
||||
settings.Size = Platform.DesktopSize * 0.75f;
|
||||
settings.MinimumSize = new Float2(200, 150);
|
||||
settings.StartPosition = WindowStartPosition.CenterScreen;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
|
||||
|
||||
if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true))
|
||||
{
|
||||
settings.HasBorder = false;
|
||||
|
||||
#if PLATFORM_WINDOWS && !PLATFORM_SDL
|
||||
// Skip OS sizing frame and implement it using LeftButtonHit
|
||||
settings.HasSizingFrame = false;
|
||||
#endif
|
||||
}
|
||||
#elif PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX && !PLATFORM_SDL
|
||||
settings.HasBorder = false;
|
||||
#endif
|
||||
MainWindow = Platform.CreateWindow(ref settings);
|
||||
|
||||
@@ -160,21 +160,47 @@ namespace FlaxEditor.Options
|
||||
GameWindowThenRestore,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for type of window decorations to use.
|
||||
/// </summary>
|
||||
public enum WindowDecorationsType
|
||||
{
|
||||
/// <summary>
|
||||
/// Determined automatically based on the system and any known compatibility issues with native decorations.
|
||||
/// </summary>
|
||||
Auto,
|
||||
|
||||
/// <summary>
|
||||
/// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window.
|
||||
/// </summary>
|
||||
[EditorDisplay(Name = "Auto (Child Only)")]
|
||||
AutoChildOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Use native system window decorations on all windows.
|
||||
/// </summary>
|
||||
Native,
|
||||
|
||||
/// <summary>
|
||||
/// Use custom client-side window decorations on all windows.
|
||||
/// </summary>
|
||||
[EditorDisplay(Name = "Client-side")]
|
||||
ClientSide,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f), Limit(0.1f, 10.0f)]
|
||||
[EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")]
|
||||
public float InterfaceScale { get; set; } = 1.0f;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
|
||||
/// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar. Editor restart required.")]
|
||||
public bool UseNativeWindowSystem { get; set; } = false;
|
||||
#endif
|
||||
[DefaultValue(WindowDecorationsType.AutoChildOnly)]
|
||||
[EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar decorations. Editor restart required.")]
|
||||
public WindowDecorationsType WindowDecorations { get; set; } = WindowDecorationsType.AutoChildOnly;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show selected camera preview in the editor window.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Core/Math/Quaternion.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include <ThirdParty/pugixml/pugixml.hpp>
|
||||
@@ -327,6 +328,7 @@ ProjectInfo* ProjectInfo::Load(const String& path)
|
||||
}
|
||||
|
||||
// Load
|
||||
PROFILE_MEM(Editor);
|
||||
auto project = New<ProjectInfo>();
|
||||
if (project->LoadProject(path))
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace FlaxEditor.SceneGraph
|
||||
/// <summary>
|
||||
/// The parent node.
|
||||
/// </summary>
|
||||
protected SceneGraphNode parentNode;
|
||||
internal SceneGraphNode parentNode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children list.
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Platform/Thread.h"
|
||||
#include "Engine/Threading/IRunnable.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
|
||||
void OnAsyncBegin(Thread* thread);
|
||||
void OnAsyncEnd();
|
||||
@@ -232,6 +233,8 @@ void OnAsyncEnd()
|
||||
|
||||
bool CodeEditingManagerService::Init()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Try get editors
|
||||
#if USE_VISUAL_STUDIO_DTE
|
||||
VisualStudioEditor::FindEditors(&CodeEditors);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
|
||||
@@ -77,7 +78,7 @@ namespace ScriptsBuilderImpl
|
||||
void onScriptsReloadEnd();
|
||||
void onScriptsLoaded();
|
||||
|
||||
void GetClassName(const StringAnsi& fullname, StringAnsi& className);
|
||||
void GetClassName(const StringAnsiView fullname, StringAnsi& className);
|
||||
|
||||
void onCodeEditorAsyncOpenBegin()
|
||||
{
|
||||
@@ -120,9 +121,13 @@ void ScriptsBuilderImpl::sourceDirEvent(const String& path, FileSystemAction act
|
||||
// Discard non-source files or generated files
|
||||
if ((!path.EndsWith(TEXT(".cs")) &&
|
||||
!path.EndsWith(TEXT(".cpp")) &&
|
||||
!path.EndsWith(TEXT(".c")) &&
|
||||
!path.EndsWith(TEXT(".hpp")) &&
|
||||
!path.EndsWith(TEXT(".h"))) ||
|
||||
path.EndsWith(TEXT(".Gen.cs")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScopeLock scopeLock(_locker);
|
||||
_lastSourceCodeEdited = DateTime::Now();
|
||||
@@ -272,7 +277,7 @@ bool ScriptsBuilder::GenerateProject(const StringView& customArgs)
|
||||
return RunBuildTool(args);
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::GetClassName(const StringAnsi& fullname, StringAnsi& className)
|
||||
void ScriptsBuilderImpl::GetClassName(const StringAnsiView fullname, StringAnsi& className)
|
||||
{
|
||||
const auto lastDotIndex = fullname.FindLast('.');
|
||||
if (lastDotIndex != -1)
|
||||
@@ -413,6 +418,7 @@ void ScriptsBuilder::GetBinariesConfiguration(const Char*& target, const Char*&
|
||||
|
||||
bool ScriptsBuilderImpl::compileGameScriptsAsyncInner()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
LOG(Info, "Starting scripts compilation...");
|
||||
CallEvent(EventType::CompileStarted);
|
||||
|
||||
@@ -519,6 +525,8 @@ void ScriptsBuilderImpl::onEditorAssemblyUnloading(MAssembly* assembly)
|
||||
|
||||
bool ScriptsBuilderImpl::compileGameScriptsAsync()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Start
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
@@ -562,6 +570,7 @@ bool ScriptsBuilderService::Init()
|
||||
// Check flag
|
||||
if (_isInited)
|
||||
return false;
|
||||
PROFILE_MEM(Editor);
|
||||
_isInited = true;
|
||||
|
||||
// Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors
|
||||
@@ -659,6 +668,9 @@ bool ScriptsBuilderService::Init()
|
||||
|
||||
void ScriptsBuilderService::Update()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Editor);
|
||||
|
||||
// Send compilation events
|
||||
{
|
||||
ScopeLock scopeLock(_compileEventsLocker);
|
||||
|
||||
@@ -6,6 +6,7 @@ using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
@@ -123,7 +124,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
case MaterialDomain.Particle:
|
||||
case MaterialDomain.Deformable:
|
||||
{
|
||||
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit;
|
||||
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit && info.ShadingModel != MaterialShadingModel.CustomLit;
|
||||
bool isOpaque = info.BlendMode == MaterialBlendMode.Opaque;
|
||||
bool withTess = info.TessellationMode != TessellationMethod.None;
|
||||
|
||||
GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit;
|
||||
@@ -134,8 +136,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit;
|
||||
GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit;
|
||||
GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit;
|
||||
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || info.BlendMode != MaterialBlendMode.Opaque;
|
||||
GetBox(MaterialNodeBoxes.Refraction).IsActive = info.BlendMode != MaterialBlendMode.Opaque;
|
||||
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || !isOpaque;
|
||||
GetBox(MaterialNodeBoxes.Refraction).IsActive = !isOpaque;
|
||||
GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true;
|
||||
GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess;
|
||||
GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess;
|
||||
@@ -260,6 +262,211 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
#if false // TODO: finish code editor based on RichTextBoxBase with text block parsing for custom styling
|
||||
internal sealed class CustomCodeTextBox : RichTextBoxBase
|
||||
{
|
||||
protected override void OnParseTextBlocks()
|
||||
{
|
||||
base.OnParseTextBlocks();
|
||||
|
||||
// Single block for a whole text
|
||||
// TODO: implement code parsing with HLSL syntax
|
||||
var font = Style.Current.FontMedium;
|
||||
var style = new TextBlockStyle
|
||||
{
|
||||
Font = new FontReference(font),
|
||||
Color = Style.Current.Foreground,
|
||||
BackgroundSelectedBrush = new SolidColorBrush(Style.Current.BackgroundSelected),
|
||||
};
|
||||
_textBlocks.Clear();
|
||||
_textBlocks.Add(new TextBlock
|
||||
{
|
||||
Range = new TextRange
|
||||
{
|
||||
StartIndex = 0,
|
||||
EndIndex = TextLength,
|
||||
},
|
||||
Style = style,
|
||||
Bounds = new Rectangle(Float2.Zero, font.MeasureText(Text)),
|
||||
});
|
||||
}
|
||||
#else
|
||||
internal sealed class CustomCodeTextBox : TextBox
|
||||
{
|
||||
#endif
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Draw border
|
||||
if (!IsFocused)
|
||||
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BorderNormal);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CustomCodeNode : SurfaceNode
|
||||
{
|
||||
private Rectangle _resizeButtonRect;
|
||||
private Float2 _startResizingSize;
|
||||
private Float2 _startResizingCornerOffset;
|
||||
private bool _isResizing;
|
||||
private CustomCodeTextBox _textBox;
|
||||
|
||||
private int SizeValueIndex => Archetype.TypeID == 8 ? 1 : 3; // Index of the Size stored in Values array
|
||||
|
||||
private Float2 SizeValue
|
||||
{
|
||||
get => (Float2)Values[SizeValueIndex];
|
||||
set => SetValue(SizeValueIndex, value, false);
|
||||
}
|
||||
|
||||
public CustomCodeNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
Float2 pos = new Float2(FlaxEditor.Surface.Constants.NodeMarginX, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize), size;
|
||||
if (nodeArch.TypeID == 8)
|
||||
{
|
||||
pos += new Float2(60, 0);
|
||||
size = new Float2(172, 200);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += new Float2(0, 40);
|
||||
size = new Float2(300, 200);
|
||||
}
|
||||
_textBox = new CustomCodeTextBox
|
||||
{
|
||||
IsMultiline = true,
|
||||
Location = pos,
|
||||
Size = size,
|
||||
Parent = this,
|
||||
AnchorMax = Float2.One,
|
||||
};
|
||||
_textBox.EditEnd += () => SetValue(0, _textBox.Text);
|
||||
}
|
||||
|
||||
public override bool CanSelect(ref Float2 location)
|
||||
{
|
||||
return base.CanSelect(ref location) && !_resizeButtonRect.MakeOffsetted(Location).Contains(ref location);
|
||||
}
|
||||
|
||||
public override void OnSurfaceLoaded(SurfaceNodeActions action)
|
||||
{
|
||||
base.OnSurfaceLoaded(action);
|
||||
|
||||
_textBox.Text = (string)Values[0];
|
||||
|
||||
var size = SizeValue;
|
||||
if (Surface != null && Surface.GridSnappingEnabled)
|
||||
size = Surface.SnapToGrid(size, true);
|
||||
Resize(size.X, size.Y);
|
||||
}
|
||||
|
||||
public override void OnValuesChanged()
|
||||
{
|
||||
base.OnValuesChanged();
|
||||
|
||||
var size = SizeValue;
|
||||
Resize(size.X, size.Y);
|
||||
_textBox.Text = (string)Values[0];
|
||||
}
|
||||
|
||||
protected override void UpdateRectangles()
|
||||
{
|
||||
base.UpdateRectangles();
|
||||
|
||||
const float buttonMargin = FlaxEditor.Surface.Constants.NodeCloseButtonMargin;
|
||||
const float buttonSize = FlaxEditor.Surface.Constants.NodeCloseButtonSize;
|
||||
_resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin - 4, buttonSize, buttonSize);
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
var style = Style.Current;
|
||||
if (_isResizing)
|
||||
{
|
||||
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||
}
|
||||
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
|
||||
public override void OnLostFocus()
|
||||
{
|
||||
if (_isResizing)
|
||||
EndResizing();
|
||||
|
||||
base.OnLostFocus();
|
||||
}
|
||||
|
||||
public override void OnEndMouseCapture()
|
||||
{
|
||||
if (_isResizing)
|
||||
EndResizing();
|
||||
|
||||
base.OnEndMouseCapture();
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDown(location, button))
|
||||
return true;
|
||||
|
||||
if (button == MouseButton.Left && _resizeButtonRect.Contains(ref location) && Surface.CanEdit)
|
||||
{
|
||||
// Start sliding
|
||||
_isResizing = true;
|
||||
_startResizingSize = Size;
|
||||
_startResizingCornerOffset = Size - location;
|
||||
StartMouseCapture();
|
||||
Cursor = CursorType.SizeNWSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
if (_isResizing)
|
||||
{
|
||||
var emptySize = CalculateNodeSize(0, 0);
|
||||
var size = Float2.Max(location - emptySize + _startResizingCornerOffset, new Float2(240, 160));
|
||||
Resize(size.X, size.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _isResizing)
|
||||
{
|
||||
EndResizing();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
private void EndResizing()
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
EndMouseCapture();
|
||||
_isResizing = false;
|
||||
if (_startResizingSize != Size)
|
||||
{
|
||||
var emptySize = CalculateNodeSize(0, 0);
|
||||
SizeValue = Size - emptySize;
|
||||
Surface.MarkAsEdited(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum MaterialTemplateInputsMapping
|
||||
{
|
||||
/// <summary>
|
||||
@@ -410,13 +617,15 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 8,
|
||||
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
|
||||
Title = "Custom Code",
|
||||
Description = "Custom HLSL shader code expression",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
Size = new Float2(300, 200),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
"// Here you can add HLSL code\nOutput0 = Input0;"
|
||||
"// Here you can add HLSL code\nOutput0 = Input0;",
|
||||
new Float2(300, 200),
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
@@ -433,8 +642,6 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9),
|
||||
NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10),
|
||||
NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11),
|
||||
|
||||
NodeElementArchetype.Factory.TextBox(60, 0, 175, 200, 0),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
@@ -874,6 +1081,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 38,
|
||||
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
|
||||
Title = "Custom Global Code",
|
||||
Description = "Custom global HLSL shader code expression (placed before material shader code). Can contain includes to shader utilities or declare functions to reuse later.",
|
||||
Flags = NodeFlags.MaterialGraph,
|
||||
@@ -883,6 +1091,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
"// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}",
|
||||
true,
|
||||
(int)MaterialTemplateInputsMapping.Utilities,
|
||||
new Float2(300, 240),
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
@@ -890,7 +1099,6 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
|
||||
NodeElementArchetype.Factory.Text(0, 20, "Location"),
|
||||
NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)),
|
||||
NodeElementArchetype.Factory.TextBox(0, 40, 300, 200, 0),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
// Copyright (c) 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"
|
||||
|
||||
@@ -1559,5 +1559,17 @@ namespace FlaxEditor.Utilities
|
||||
c = c.Parent;
|
||||
return c as ISceneEditingContext;
|
||||
}
|
||||
|
||||
internal static bool UseCustomWindowDecorations(bool isMainWindow = false)
|
||||
{
|
||||
return Editor.Instance.Options.Options.Interface.WindowDecorations switch
|
||||
{
|
||||
Options.InterfaceOptions.WindowDecorationsType.Auto => !Platform.SupportsNativeDecorations,
|
||||
Options.InterfaceOptions.WindowDecorationsType.AutoChildOnly => !isMainWindow ? !Platform.SupportsNativeDecorations : true,
|
||||
Options.InterfaceOptions.WindowDecorationsType.Native => false,
|
||||
Options.InterfaceOptions.WindowDecorationsType.ClientSide => true,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#include "Engine/Content/Assets/MaterialInstance.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Level/Actors/PointLight.h"
|
||||
@@ -263,6 +264,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
|
||||
|
||||
bool ViewportIconsRendererService::Init()
|
||||
{
|
||||
PROFILE_MEM(Editor);
|
||||
QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
|
||||
#define INIT(type, path) \
|
||||
InstanceBuffers[static_cast<int32>(IconTypes::type)].Setup(1); \
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Docking;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
@@ -158,18 +159,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).
|
||||
@@ -522,10 +527,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;
|
||||
@@ -1520,7 +1526,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);
|
||||
@@ -1529,6 +1537,7 @@ namespace FlaxEditor.Viewport
|
||||
_viewMousePos = center;
|
||||
win.MousePosition = PointToWindow(_viewMousePos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1540,6 +1549,7 @@ namespace FlaxEditor.Viewport
|
||||
// Restore cursor and stop tracking mouse movement
|
||||
win.Cursor = CursorType.Default;
|
||||
win.EndTrackingMouse();
|
||||
win.MouseMoveRelative -= OnMouseMoveRelative;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1621,18 +1631,15 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Get parent window
|
||||
var win = (WindowRootControl)Root;
|
||||
|
||||
// Get current mouse position in the view
|
||||
if (win.IsFocused)
|
||||
{
|
||||
// When the window is not focused, the position in window does not return sane values
|
||||
Float2 pos = PointFromWindow(win.MousePosition);
|
||||
if (!float.IsInfinity(pos.LengthSquared))
|
||||
_viewMousePos = pos;
|
||||
// Get current mouse position in the view
|
||||
_viewMousePos = PointFromWindow(win.MousePosition);
|
||||
}
|
||||
|
||||
// Update input
|
||||
var window = win.Window;
|
||||
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow;
|
||||
var canUseInput = window != null && window.IsFocused && window.IsForegroundWindow && !WindowDragHelper.IsDragActive;
|
||||
{
|
||||
// Get input buttons and keys (skip if viewport has no focus or mouse is over a child control)
|
||||
var isViewportControllingMouse = canUseInput && IsControllingMouse;
|
||||
@@ -1644,9 +1651,17 @@ namespace FlaxEditor.Viewport
|
||||
else
|
||||
EndMouseCapture();
|
||||
}
|
||||
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
|
||||
|
||||
_prevInput = _input;
|
||||
#if PLATFORM_SDL
|
||||
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos) || _prevInput.IsControllingMouse;
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
if (_prevInput.IsControllingMouse)
|
||||
hit = null;
|
||||
#else
|
||||
bool useMouse = IsControllingMouse || ContainsPoint(ref _viewMousePos);
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
|
||||
#endif
|
||||
if (canUseInput && ContainsFocus && hit == null)
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
@@ -1759,6 +1774,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)
|
||||
@@ -1800,6 +1819,7 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
|
||||
_mouseDeltaLast = currentDelta;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update
|
||||
moveDelta *= dt * (60.0f * 4.0f);
|
||||
@@ -1808,12 +1828,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)
|
||||
@@ -1825,6 +1847,8 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
#else
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
@@ -1839,6 +1863,7 @@ namespace FlaxEditor.Viewport
|
||||
_mouseDelta = Float2.Zero;
|
||||
}
|
||||
_mouseDeltaLast = Float2.Zero;
|
||||
#endif
|
||||
|
||||
if (ContainsFocus)
|
||||
{
|
||||
@@ -1888,6 +1913,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)
|
||||
{
|
||||
@@ -1953,7 +1984,8 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
if (_isControllingMouse)
|
||||
{
|
||||
OnControlMouseEnd(RootWindow.Window);
|
||||
if (RootWindow?.Window != null)
|
||||
OnControlMouseEnd(RootWindow.Window);
|
||||
_isControllingMouse = false;
|
||||
_isVirtualMouseRightDown = false;
|
||||
}
|
||||
|
||||
@@ -611,16 +611,25 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled
|
||||
bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) &&
|
||||
Gizmos?.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse;
|
||||
Gizmos?.Active is TransformGizmo;
|
||||
_rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnControlMouseBegin(Window win)
|
||||
{
|
||||
_rubberBandSelector.ReleaseRubberBandSelection();
|
||||
|
||||
base.OnControlMouseBegin(win);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnLeftMouseButtonDown()
|
||||
{
|
||||
base.OnLeftMouseButtonDown();
|
||||
|
||||
_rubberBandSelector.TryStartingRubberBandSelection();
|
||||
if (!IsAltKeyDown)
|
||||
_rubberBandSelector.TryStartingRubberBandSelection();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -129,6 +129,9 @@ namespace FlaxEditor.Windows
|
||||
"Mono Project - www.mono-project.com",
|
||||
#if USE_NETCORE
|
||||
".NET - www.dotnet.microsoft.com",
|
||||
#endif
|
||||
#if PLATFORM_SDL
|
||||
"Simple DirectMedia Layer - www.libsdl.org",
|
||||
#endif
|
||||
"FreeType Project - www.freetype.org",
|
||||
"Assimp - www.assimp.sourceforge.net",
|
||||
|
||||
@@ -1063,7 +1063,7 @@ namespace FlaxEditor.Windows
|
||||
_cursorVisible = Screen.CursorVisible;
|
||||
_cursorLockMode = Screen.CursorLock;
|
||||
Screen.CursorVisible = true;
|
||||
if (Screen.CursorLock == CursorLockMode.Clipped)
|
||||
if (Screen.CursorLock == CursorLockMode.Clipped || Screen.CursorLock == CursorLockMode.Locked)
|
||||
Screen.CursorLock = CursorLockMode.None;
|
||||
|
||||
// Defocus
|
||||
@@ -1152,8 +1152,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;
|
||||
@@ -1178,9 +1181,10 @@ namespace FlaxEditor.Windows
|
||||
_cursorVisible = Screen.CursorVisible;
|
||||
_cursorLockMode = Screen.CursorLock;
|
||||
|
||||
// Restore cursor visibility (could be hidden by the game)
|
||||
// Restore cursor state, could be hidden or locked by the game
|
||||
if (!_cursorVisible)
|
||||
Screen.CursorVisible = true;
|
||||
Screen.CursorLock = CursorLockMode.None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
private List<ClickableRow> _tableRowsCache;
|
||||
private Dictionary<Guid, Resource> _resourceCache;
|
||||
private StringBuilder _stringBuilder;
|
||||
private Asset[] _assetsCache;
|
||||
|
||||
public Assets()
|
||||
: base("Assets")
|
||||
@@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler
|
||||
_stringBuilder = new StringBuilder();
|
||||
|
||||
// Capture current assets usage info
|
||||
var assets = FlaxEngine.Content.Assets;
|
||||
var resources = new Resource[assets.Length];
|
||||
FlaxEngine.Content.GetAssets(ref _assetsCache, out var count);
|
||||
var resources = new Resource[count];
|
||||
ulong totalMemoryUsage = 0;
|
||||
for (int i = 0; i < resources.Length; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var asset = assets[i];
|
||||
var asset = _assetsCache[i];
|
||||
ref var resource = ref resources[i];
|
||||
if (!asset)
|
||||
continue;
|
||||
@@ -179,6 +180,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
if (_resources == null)
|
||||
_resources = new SamplesBuffer<Resource[]>();
|
||||
_resources.Add(resources);
|
||||
Array.Clear(_assetsCache);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -200,6 +202,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
_resourceCache?.Clear();
|
||||
_tableRowsCache?.Clear();
|
||||
_stringBuilder?.Clear();
|
||||
_assetsCache = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#if USE_PROFILER
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
@@ -13,9 +15,22 @@ namespace FlaxEditor.Windows.Profiler
|
||||
/// <seealso cref="FlaxEditor.Windows.Profiler.ProfilerMode" />
|
||||
internal sealed class Memory : ProfilerMode
|
||||
{
|
||||
private struct FrameData
|
||||
{
|
||||
public ProfilerMemory.GroupsArray Usage;
|
||||
public ProfilerMemory.GroupsArray Peek;
|
||||
public ProfilerMemory.GroupsArray Count;
|
||||
}
|
||||
|
||||
private readonly SingleChart _nativeAllocationsChart;
|
||||
private readonly SingleChart _managedAllocationsChart;
|
||||
|
||||
private readonly Table _table;
|
||||
private SamplesBuffer<FrameData> _frames;
|
||||
private List<Row> _tableRowsCache;
|
||||
private string[] _groupNames;
|
||||
private int[] _groupOrder;
|
||||
private Label _warningText;
|
||||
|
||||
public Memory()
|
||||
: base("Memory")
|
||||
{
|
||||
@@ -50,6 +65,70 @@ namespace FlaxEditor.Windows.Profiler
|
||||
Parent = layout,
|
||||
};
|
||||
_managedAllocationsChart.SelectedSampleChanged += OnSelectedSampleChanged;
|
||||
|
||||
// Warning text
|
||||
if (!ProfilerMemory.Enabled)
|
||||
{
|
||||
_warningText = new Label
|
||||
{
|
||||
Text = "Detailed memory profiling is disabled. Run with command line '-mem'",
|
||||
TextColor = Color.Red,
|
||||
Visible = false,
|
||||
Parent = layout,
|
||||
};
|
||||
}
|
||||
|
||||
// Table
|
||||
var style = Style.Current;
|
||||
var headerColor = style.LightBackground;
|
||||
var textColor = style.Foreground;
|
||||
_table = new Table
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new ColumnDefinition
|
||||
{
|
||||
UseExpandCollapseMode = true,
|
||||
CellAlignment = TextAlignment.Near,
|
||||
Title = "Group",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Usage",
|
||||
TitleBackgroundColor = headerColor,
|
||||
FormatValue = FormatCellBytes,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Peek",
|
||||
TitleBackgroundColor = headerColor,
|
||||
FormatValue = FormatCellBytes,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
new ColumnDefinition
|
||||
{
|
||||
Title = "Count",
|
||||
TitleBackgroundColor = headerColor,
|
||||
TitleColor = textColor,
|
||||
},
|
||||
},
|
||||
Parent = layout,
|
||||
};
|
||||
_table.Splits = new[]
|
||||
{
|
||||
0.5f,
|
||||
0.2f,
|
||||
0.2f,
|
||||
0.1f,
|
||||
};
|
||||
}
|
||||
|
||||
private string FormatCellBytes(object x)
|
||||
{
|
||||
return Utilities.Utils.FormatBytesCount(Convert.ToUInt64(x));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -57,6 +136,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
_nativeAllocationsChart.Clear();
|
||||
_managedAllocationsChart.Clear();
|
||||
_frames?.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -84,6 +164,19 @@ namespace FlaxEditor.Windows.Profiler
|
||||
|
||||
_nativeAllocationsChart.AddSample(nativeMemoryAllocation);
|
||||
_managedAllocationsChart.AddSample(managedMemoryAllocation);
|
||||
|
||||
// Gather memory profiler stats for groups
|
||||
var frame = new FrameData
|
||||
{
|
||||
Usage = ProfilerMemory.GetGroups(0),
|
||||
Peek = ProfilerMemory.GetGroups(1),
|
||||
Count = ProfilerMemory.GetGroups(2),
|
||||
};
|
||||
if (_frames == null)
|
||||
_frames = new SamplesBuffer<FrameData>();
|
||||
if (_groupNames == null)
|
||||
_groupNames = ProfilerMemory.GetGroupNames();
|
||||
_frames.Add(frame);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -91,6 +184,112 @@ namespace FlaxEditor.Windows.Profiler
|
||||
{
|
||||
_nativeAllocationsChart.SelectedSampleIndex = selectedFrame;
|
||||
_managedAllocationsChart.SelectedSampleIndex = selectedFrame;
|
||||
|
||||
UpdateTable(selectedFrame);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_tableRowsCache?.Clear();
|
||||
_groupNames = null;
|
||||
_groupOrder = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private void UpdateTable(int selectedFrame)
|
||||
{
|
||||
if (_frames == null)
|
||||
return;
|
||||
if (_tableRowsCache == null)
|
||||
_tableRowsCache = new List<Row>();
|
||||
_table.IsLayoutLocked = true;
|
||||
|
||||
RecycleTableRows(_table, _tableRowsCache);
|
||||
UpdateTableInner(selectedFrame);
|
||||
|
||||
_table.UnlockChildrenRecursive();
|
||||
_table.PerformLayout();
|
||||
}
|
||||
|
||||
private unsafe void UpdateTableInner(int selectedFrame)
|
||||
{
|
||||
if (_frames.Count == 0)
|
||||
return;
|
||||
if (_warningText != null)
|
||||
_warningText.Visible = true;
|
||||
var frame = _frames.Get(selectedFrame);
|
||||
var totalUage = frame.Usage.Values0[(int)ProfilerMemory.Groups.TotalTracked];
|
||||
var totalPeek = frame.Peek.Values0[(int)ProfilerMemory.Groups.TotalTracked];
|
||||
var totalCount = frame.Count.Values0[(int)ProfilerMemory.Groups.TotalTracked];
|
||||
|
||||
// Sort by memory size
|
||||
if (_groupOrder == null)
|
||||
_groupOrder = new int[(int)ProfilerMemory.Groups.MAX];
|
||||
for (int i = 0; i < (int)ProfilerMemory.Groups.MAX; i++)
|
||||
_groupOrder[i] = i;
|
||||
Array.Sort(_groupOrder, (x, y) =>
|
||||
{
|
||||
var tmp = _frames.Get(selectedFrame);
|
||||
return tmp.Usage.Values0[y].CompareTo(tmp.Usage.Values0[x]);
|
||||
});
|
||||
|
||||
// Add rows
|
||||
var rowColor2 = Style.Current.Background * 1.4f;
|
||||
for (int i = 0; i < (int)ProfilerMemory.Groups.MAX; i++)
|
||||
{
|
||||
var group = _groupOrder[i];
|
||||
var groupUsage = frame.Usage.Values0[group];
|
||||
if (groupUsage <= 0)
|
||||
continue;
|
||||
var groupPeek = frame.Peek.Values0[group];
|
||||
var groupCount = frame.Count.Values0[group];
|
||||
|
||||
Row row;
|
||||
if (_tableRowsCache.Count != 0)
|
||||
{
|
||||
var last = _tableRowsCache.Count - 1;
|
||||
row = _tableRowsCache[last];
|
||||
_tableRowsCache.RemoveAt(last);
|
||||
}
|
||||
else
|
||||
{
|
||||
row = new Row
|
||||
{
|
||||
Values = new object[4],
|
||||
BackgroundColors = new Color[4],
|
||||
};
|
||||
}
|
||||
{
|
||||
// Group
|
||||
row.Values[0] = _groupNames[group];
|
||||
|
||||
// Usage
|
||||
row.Values[1] = groupUsage;
|
||||
row.BackgroundColors[1] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupUsage / totalUage) * 0.5f);
|
||||
|
||||
// Peek
|
||||
row.Values[2] = groupPeek;
|
||||
row.BackgroundColors[2] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupPeek / totalPeek) * 0.5f);
|
||||
|
||||
// Count
|
||||
row.Values[3] = groupCount;
|
||||
row.BackgroundColors[3] = Color.Red.AlphaMultiplied(Mathf.Min(1, (float)groupCount / totalCount) * 0.5f);
|
||||
}
|
||||
row.Width = _table.Width;
|
||||
row.BackgroundColor = i % 2 == 1 ? rowColor2 : Color.Transparent;
|
||||
row.Parent = _table;
|
||||
|
||||
var useBackground = group != (int)ProfilerMemory.Groups.Total &&
|
||||
group != (int)ProfilerMemory.Groups.TotalTracked &&
|
||||
group != (int)ProfilerMemory.Groups.Malloc;
|
||||
if (!useBackground)
|
||||
{
|
||||
for (int k = 1; k < row.BackgroundColors.Length; k++)
|
||||
row.BackgroundColors[k] = Color.Transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
private Dictionary<string, Guid> _assetPathToId;
|
||||
private Dictionary<Guid, Resource> _resourceCache;
|
||||
private StringBuilder _stringBuilder;
|
||||
private GPUResource[] _gpuResourcesCached;
|
||||
|
||||
public MemoryGPU()
|
||||
: base("GPU Memory")
|
||||
@@ -138,13 +139,15 @@ namespace FlaxEditor.Windows.Profiler
|
||||
|
||||
// Capture current GPU resources usage info
|
||||
var contentDatabase = Editor.Instance.ContentDatabase;
|
||||
var gpuResources = GPUDevice.Instance.Resources;
|
||||
var resources = new Resource[gpuResources.Length];
|
||||
GPUDevice.Instance.GetResources(ref _gpuResourcesCached, out var count);
|
||||
var resources = new Resource[count];
|
||||
var sb = _stringBuilder;
|
||||
for (int i = 0; i < resources.Length; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var gpuResource = gpuResources[i];
|
||||
var gpuResource = _gpuResourcesCached[i];
|
||||
ref var resource = ref resources[i];
|
||||
if (!gpuResource)
|
||||
continue;
|
||||
|
||||
// Try to reuse cached resource info
|
||||
var gpuResourceId = gpuResource.ID;
|
||||
@@ -219,6 +222,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
if (_resources == null)
|
||||
_resources = new SamplesBuffer<Resource[]>();
|
||||
_resources.Add(resources);
|
||||
Array.Clear(_gpuResourcesCached);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -255,6 +259,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
_assetPathToId?.Clear();
|
||||
_tableRowsCache?.Clear();
|
||||
_stringBuilder?.Clear();
|
||||
_gpuResourcesCached = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace FlaxEditor.Windows
|
||||
private Tree _tree;
|
||||
private Panel _sceneTreePanel;
|
||||
private bool _isUpdatingSelection;
|
||||
private bool _isMouseDown;
|
||||
private bool _blockSceneTreeScroll = false;
|
||||
|
||||
private DragAssets _dragAssets;
|
||||
@@ -374,10 +373,7 @@ namespace FlaxEditor.Windows
|
||||
return true;
|
||||
|
||||
if (buttons == MouseButton.Right)
|
||||
{
|
||||
_isMouseDown = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -388,10 +384,8 @@ namespace FlaxEditor.Windows
|
||||
if (base.OnMouseUp(location, buttons))
|
||||
return true;
|
||||
|
||||
if (_isMouseDown && buttons == MouseButton.Right)
|
||||
if (buttons == MouseButton.Right)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
|
||||
if (Editor.StateMachine.CurrentState.CanEditScene)
|
||||
{
|
||||
// Show context menu
|
||||
@@ -416,14 +410,6 @@ namespace FlaxEditor.Windows
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLostFocus()
|
||||
{
|
||||
_isMouseDown = false;
|
||||
|
||||
base.OnLostFocus();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Threading/TaskGraph.h"
|
||||
|
||||
class BehaviorSystem : public TaskGraphSystem
|
||||
@@ -38,6 +39,7 @@ TaskGraphSystem* Behavior::System = nullptr;
|
||||
void BehaviorSystem::Job(int32 index)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Behavior.Job");
|
||||
PROFILE_MEM(AI);
|
||||
Behaviors[index]->UpdateAsync();
|
||||
}
|
||||
|
||||
@@ -57,6 +59,7 @@ void BehaviorSystem::Execute(TaskGraph* graph)
|
||||
|
||||
bool BehaviorService::Init()
|
||||
{
|
||||
PROFILE_MEM(AI);
|
||||
Behavior::System = New<BehaviorSystem>();
|
||||
Engine::UpdateGraph->AddSystem(Behavior::System);
|
||||
return false;
|
||||
@@ -70,9 +73,9 @@ void BehaviorService::Dispose()
|
||||
|
||||
Behavior::Behavior(const SpawnParams& params)
|
||||
: Script(params)
|
||||
, Tree(this)
|
||||
{
|
||||
_knowledge.Behavior = this;
|
||||
Tree.Changed.Bind<Behavior, &Behavior::ResetLogic>(this);
|
||||
}
|
||||
|
||||
void Behavior::UpdateAsync()
|
||||
@@ -172,6 +175,19 @@ void Behavior::OnDisable()
|
||||
BehaviorServiceInstance.UpdateList.Remove(this);
|
||||
}
|
||||
|
||||
void Behavior::OnAssetChanged(Asset* asset, void* caller)
|
||||
{
|
||||
ResetLogic();
|
||||
}
|
||||
|
||||
void Behavior::OnAssetLoaded(Asset* asset, void* caller)
|
||||
{
|
||||
}
|
||||
|
||||
void Behavior::OnAssetUnloaded(Asset* asset, void* caller)
|
||||
{
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/// <summary>
|
||||
/// Behavior instance script that runs Behavior Tree execution.
|
||||
/// </summary>
|
||||
API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script
|
||||
API_CLASS(Attributes="Category(\"Flax Engine\")") class FLAXENGINE_API Behavior : public Script, private IAssetReference
|
||||
{
|
||||
API_AUTO_SERIALIZATION();
|
||||
DECLARE_SCRIPTING_TYPE(Behavior);
|
||||
@@ -92,6 +92,11 @@ public:
|
||||
void OnDisable() override;
|
||||
|
||||
private:
|
||||
// [IAssetReference]
|
||||
void OnAssetChanged(Asset* asset, void* caller) override;
|
||||
void OnAssetLoaded(Asset* asset, void* caller) override;
|
||||
void OnAssetUnloaded(Asset* asset, void* caller) override;
|
||||
|
||||
#if USE_EDITOR
|
||||
// Editor-only utilities to debug nodes state.
|
||||
API_FUNCTION(Internal) static bool GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "BehaviorTree.h"
|
||||
#include "BehaviorTreeNodes.h"
|
||||
#include "BehaviorKnowledgeSelector.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/BinaryModule.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MProperty.h"
|
||||
@@ -144,6 +145,7 @@ BehaviorKnowledge::~BehaviorKnowledge()
|
||||
|
||||
void BehaviorKnowledge::InitMemory(BehaviorTree* tree)
|
||||
{
|
||||
PROFILE_MEM(AI);
|
||||
if (Tree)
|
||||
FreeMemory();
|
||||
if (!tree)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Engine/Serialization/JsonSerializer.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Level/Level.h"
|
||||
@@ -275,6 +276,7 @@ Asset::LoadResult BehaviorTree::load()
|
||||
if (surfaceChunk == nullptr)
|
||||
return LoadResult::MissingDataChunk;
|
||||
MemoryReadStream surfaceStream(surfaceChunk->Get(), surfaceChunk->Size());
|
||||
PROFILE_MEM(AI);
|
||||
if (Graph.Load(&surfaceStream, true))
|
||||
{
|
||||
LOG(Warning, "Failed to load graph \'{0}\'", ToString());
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "AnimEvent.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Level/Actors/AnimatedModel.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
@@ -69,6 +70,7 @@ AnimContinuousEvent::AnimContinuousEvent(const SpawnParams& params)
|
||||
|
||||
bool AnimationsService::Init()
|
||||
{
|
||||
PROFILE_MEM(Animations);
|
||||
Animations::System = New<AnimationsSystem>();
|
||||
Engine::UpdateGraph->AddSystem(Animations::System);
|
||||
return false;
|
||||
@@ -83,6 +85,7 @@ void AnimationsService::Dispose()
|
||||
void AnimationsSystem::Job(int32 index)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Animations.Job");
|
||||
PROFILE_MEM(Animations);
|
||||
auto animatedModel = AnimationManagerInstance.UpdateList[index];
|
||||
if (CanUpdateModel(animatedModel))
|
||||
{
|
||||
@@ -147,6 +150,7 @@ void AnimationsSystem::PostExecute(TaskGraph* graph)
|
||||
if (!Active)
|
||||
return;
|
||||
PROFILE_CPU_NAMED("Animations.PostExecute");
|
||||
PROFILE_MEM(Animations);
|
||||
|
||||
// Update gameplay
|
||||
for (int32 index = 0; index < AnimationManagerInstance.UpdateList.Count(); index++)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Content/Assets/SkinnedModel.h"
|
||||
#include "Engine/Graphics/Models/SkeletonData.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
|
||||
extern void RetargetSkeletonPose(const SkeletonData& sourceSkeleton, const SkeletonData& targetSkeleton, const SkinnedModel::SkeletonMapping& mapping, const Transform* sourceNodes, Transform* targetNodes);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Audio/AudioClip.h"
|
||||
#include "Engine/Graphics/PostProcessSettings.h"
|
||||
#if USE_EDITOR
|
||||
@@ -249,6 +250,7 @@ bool SceneAnimation::Save(const StringView& path)
|
||||
Asset::LoadResult SceneAnimation::load()
|
||||
{
|
||||
TrackStatesCount = 0;
|
||||
PROFILE_MEM(AnimationsData);
|
||||
|
||||
// Get the data chunk
|
||||
if (LoadChunk(0))
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Engine/Audio/AudioSource.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MException.h"
|
||||
@@ -151,6 +152,7 @@ void SceneAnimationPlayer::Tick(float dt)
|
||||
SceneAnimation* anim = Animation.Get();
|
||||
if (!anim || !anim->IsLoaded())
|
||||
return;
|
||||
PROFILE_MEM(Animations);
|
||||
|
||||
// Setup state
|
||||
if (_tracks.Count() != anim->TrackStatesCount)
|
||||
@@ -229,6 +231,7 @@ void SceneAnimationPlayer::MapTrack(const StringView& from, const Guid& to)
|
||||
SceneAnimation* anim = Animation.Get();
|
||||
if (!anim || !anim->IsLoaded())
|
||||
return;
|
||||
PROFILE_MEM(Animations);
|
||||
for (int32 j = 0; j < anim->Tracks.Count(); j++)
|
||||
{
|
||||
const auto& track = anim->Tracks[j];
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Scripting/BinaryModule.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
@@ -151,6 +152,7 @@ void Audio::SetEnableHRTF(bool value)
|
||||
bool AudioService::Init()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Audio.Init");
|
||||
PROFILE_MEM(Audio);
|
||||
const auto settings = AudioSettings::Get();
|
||||
const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio;
|
||||
|
||||
@@ -211,6 +213,7 @@ bool AudioService::Init()
|
||||
void AudioService::Update()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Audio.Update");
|
||||
PROFILE_MEM(Audio);
|
||||
|
||||
// Update the master volume
|
||||
float masterVolume = MasterVolume;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||
#include "Engine/Streaming/StreamingGroup.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
|
||||
#include "Engine/Tools/AudioTool/AudioTool.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
@@ -18,6 +19,7 @@ REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClip
|
||||
|
||||
bool AudioClip::StreamingTask::Run()
|
||||
{
|
||||
PROFILE_MEM(Audio);
|
||||
AssetReference<AudioClip> ref = _asset.Get();
|
||||
if (ref == nullptr || AudioBackend::Instance == nullptr)
|
||||
return true;
|
||||
@@ -318,6 +320,7 @@ bool AudioClip::init(AssetInitData& initData)
|
||||
|
||||
Asset::LoadResult AudioClip::load()
|
||||
{
|
||||
PROFILE_MEM(Audio);
|
||||
#if !COMPILE_WITH_OGG_VORBIS
|
||||
if (AudioHeader.Format == AudioFormat::Vorbis)
|
||||
{
|
||||
|
||||
@@ -21,9 +21,8 @@ AudioSource::AudioSource(const SpawnParams& params)
|
||||
, _playOnStart(false)
|
||||
, _startTime(0.0f)
|
||||
, _allowSpatialization(true)
|
||||
, Clip(this)
|
||||
{
|
||||
Clip.Changed.Bind<AudioSource, &AudioSource::OnClipChanged>(this);
|
||||
Clip.Loaded.Bind<AudioSource, &AudioSource::OnClipLoaded>(this);
|
||||
}
|
||||
|
||||
void AudioSource::SetVolume(float value)
|
||||
@@ -264,7 +263,7 @@ void AudioSource::RequestStreamingBuffersUpdate()
|
||||
_needToUpdateStreamingBuffers = true;
|
||||
}
|
||||
|
||||
void AudioSource::OnClipChanged()
|
||||
void AudioSource::OnAssetChanged(Asset* asset, void* caller)
|
||||
{
|
||||
Stop();
|
||||
|
||||
@@ -276,7 +275,7 @@ void AudioSource::OnClipChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSource::OnClipLoaded()
|
||||
void AudioSource::OnAssetLoaded(Asset* asset, void* caller)
|
||||
{
|
||||
if (!SourceID)
|
||||
return;
|
||||
@@ -302,6 +301,10 @@ void AudioSource::OnClipLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSource::OnAssetUnloaded(Asset* asset, void* caller)
|
||||
{
|
||||
}
|
||||
|
||||
bool AudioSource::UseStreaming() const
|
||||
{
|
||||
if (Clip == nullptr || Clip->WaitForLoaded())
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
/// Whether or not an audio source is spatial is controlled by the assigned AudioClip.The volume and the pitch of a spatial audio source is controlled by its position and the AudioListener's position/direction/velocity.
|
||||
/// </remarks>
|
||||
API_CLASS(Attributes="ActorContextMenu(\"New/Audio/Audio Source\"), ActorToolbox(\"Other\")")
|
||||
class FLAXENGINE_API AudioSource : public Actor
|
||||
class FLAXENGINE_API AudioSource : public Actor, IAssetReference
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(AudioSource);
|
||||
friend class AudioStreamingHandler;
|
||||
@@ -293,8 +293,10 @@ public:
|
||||
void RequestStreamingBuffersUpdate();
|
||||
|
||||
private:
|
||||
void OnClipChanged();
|
||||
void OnClipLoaded();
|
||||
// [IAssetReference]
|
||||
void OnAssetChanged(Asset* asset, void* caller) override;
|
||||
void OnAssetLoaded(Asset* asset, void* caller) override;
|
||||
void OnAssetUnloaded(Asset* asset, void* caller) override;
|
||||
|
||||
/// <summary>
|
||||
/// Plays the audio source. Should have buffer(s) binded before.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Engine/Tools/AudioTool/AudioTool.h"
|
||||
#include "Engine/Engine/Units.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Audio/Audio.h"
|
||||
#include "Engine/Audio/AudioListener.h"
|
||||
#include "Engine/Audio/AudioSource.h"
|
||||
@@ -321,6 +322,8 @@ void AudioBackendOAL::Listener_ReinitializeAll()
|
||||
|
||||
uint32 AudioBackendOAL::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
|
||||
{
|
||||
PROFILE_MEM(Audio);
|
||||
|
||||
uint32 sourceID = 0;
|
||||
ALC::Source::Rebuild(sourceID, position, orientation, volume, pitch, pan, loop, spatial, attenuation, minDistance, doppler);
|
||||
|
||||
@@ -516,6 +519,7 @@ void AudioBackendOAL::Buffer_Delete(uint32 bufferID)
|
||||
void AudioBackendOAL::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Audio);
|
||||
|
||||
// Pick the format for the audio data (it might not be supported natively)
|
||||
ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
|
||||
@@ -625,6 +629,8 @@ AudioBackend::FeatureFlags AudioBackendOAL::Base_Features()
|
||||
|
||||
void AudioBackendOAL::Base_OnActiveDeviceChanged()
|
||||
{
|
||||
PROFILE_MEM(Audio);
|
||||
|
||||
// Cleanup
|
||||
Array<ALC::AudioSourceState> states;
|
||||
states.EnsureCapacity(Audio::Sources.Count());
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Audio/Audio.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
// Tweak Win ver
|
||||
@@ -232,6 +233,7 @@ void AudioBackendXAudio2::Listener_ReinitializeAll()
|
||||
|
||||
uint32 AudioBackendXAudio2::Source_Add(const AudioDataInfo& format, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
|
||||
{
|
||||
PROFILE_MEM(Audio);
|
||||
ScopeLock lock(XAudio2::Locker);
|
||||
|
||||
// Get first free source
|
||||
@@ -580,6 +582,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(uint32 sourceID)
|
||||
|
||||
uint32 AudioBackendXAudio2::Buffer_Create()
|
||||
{
|
||||
PROFILE_MEM(Audio);
|
||||
uint32 bufferID;
|
||||
ScopeLock lock(XAudio2::Locker);
|
||||
|
||||
@@ -618,6 +621,7 @@ void AudioBackendXAudio2::Buffer_Delete(uint32 bufferID)
|
||||
|
||||
void AudioBackendXAudio2::Buffer_Write(uint32 bufferID, byte* samples, const AudioDataInfo& info)
|
||||
{
|
||||
PROFILE_MEM(Audio);
|
||||
CHECK(info.NumChannels <= MAX_INPUT_CHANNELS);
|
||||
|
||||
XAudio2::Locker.Lock();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/LogContext.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
||||
#include "Engine/Threading/MainThreadTask.h"
|
||||
#include "Engine/Threading/ThreadLocal.h"
|
||||
@@ -34,15 +35,18 @@ bool ContentDeprecated::Clear(bool newValue)
|
||||
|
||||
#endif
|
||||
|
||||
AssetReferenceBase::AssetReferenceBase(IAssetReference* owner)
|
||||
: _owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
AssetReferenceBase::~AssetReferenceBase()
|
||||
{
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset = nullptr;
|
||||
asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
|
||||
asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
|
||||
asset->RemoveReference();
|
||||
asset->RemoveReference(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,52 +55,60 @@ String AssetReferenceBase::ToString() const
|
||||
return _asset ? _asset->ToString() : TEXT("<null>");
|
||||
}
|
||||
|
||||
void AssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
|
||||
{
|
||||
if (_owner)
|
||||
_owner->OnAssetChanged(asset, this);
|
||||
}
|
||||
|
||||
void AssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
|
||||
{
|
||||
if (_asset != asset)
|
||||
return;
|
||||
Loaded();
|
||||
if (_owner)
|
||||
_owner->OnAssetLoaded(asset, this);
|
||||
}
|
||||
|
||||
void AssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
|
||||
{
|
||||
if (_asset != asset)
|
||||
return;
|
||||
Unload();
|
||||
OnSet(nullptr);
|
||||
if (_owner)
|
||||
_owner->OnAssetUnloaded(asset, this);
|
||||
}
|
||||
|
||||
void AssetReferenceBase::OnSet(Asset* asset)
|
||||
{
|
||||
auto e = _asset;
|
||||
if (e != asset)
|
||||
{
|
||||
if (e)
|
||||
{
|
||||
e->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
|
||||
e->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
|
||||
e->RemoveReference();
|
||||
}
|
||||
e->RemoveReference(this);
|
||||
_asset = e = asset;
|
||||
if (e)
|
||||
{
|
||||
e->AddReference();
|
||||
e->OnLoaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
|
||||
e->OnUnloaded.Bind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
|
||||
}
|
||||
e->AddReference(this);
|
||||
Changed();
|
||||
if (_owner)
|
||||
_owner->OnAssetChanged(asset, this);
|
||||
if (e && e->IsLoaded())
|
||||
{
|
||||
Loaded();
|
||||
if (_owner)
|
||||
_owner->OnAssetLoaded(asset, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssetReferenceBase::OnLoaded(Asset* asset)
|
||||
{
|
||||
if (_asset != asset)
|
||||
return;
|
||||
Loaded();
|
||||
}
|
||||
|
||||
void AssetReferenceBase::OnUnloaded(Asset* asset)
|
||||
{
|
||||
if (_asset != asset)
|
||||
return;
|
||||
Unload();
|
||||
OnSet(nullptr);
|
||||
}
|
||||
|
||||
WeakAssetReferenceBase::~WeakAssetReferenceBase()
|
||||
{
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset = nullptr;
|
||||
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
|
||||
asset->RemoveReference(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,36 +117,43 @@ String WeakAssetReferenceBase::ToString() const
|
||||
return _asset ? _asset->ToString() : TEXT("<null>");
|
||||
}
|
||||
|
||||
void WeakAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
|
||||
{
|
||||
}
|
||||
|
||||
void WeakAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
|
||||
{
|
||||
}
|
||||
|
||||
void WeakAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
|
||||
{
|
||||
if (_asset != asset)
|
||||
return;
|
||||
Unload();
|
||||
asset->RemoveReference(this, true);
|
||||
_asset = nullptr;
|
||||
}
|
||||
|
||||
void WeakAssetReferenceBase::OnSet(Asset* asset)
|
||||
{
|
||||
auto e = _asset;
|
||||
if (e != asset)
|
||||
{
|
||||
if (e)
|
||||
e->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
|
||||
e->RemoveReference(this, true);
|
||||
_asset = e = asset;
|
||||
if (e)
|
||||
e->OnUnloaded.Bind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
|
||||
e->AddReference(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
void WeakAssetReferenceBase::OnUnloaded(Asset* asset)
|
||||
{
|
||||
if (_asset != asset)
|
||||
return;
|
||||
Unload();
|
||||
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
|
||||
_asset = nullptr;
|
||||
}
|
||||
|
||||
SoftAssetReferenceBase::~SoftAssetReferenceBase()
|
||||
{
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset = nullptr;
|
||||
asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
|
||||
asset->RemoveReference();
|
||||
asset->RemoveReference(this);
|
||||
}
|
||||
#if !BUILD_RELEASE
|
||||
_id = Guid::Empty;
|
||||
@@ -146,22 +165,34 @@ String SoftAssetReferenceBase::ToString() const
|
||||
return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>"));
|
||||
}
|
||||
|
||||
void SoftAssetReferenceBase::OnAssetChanged(Asset* asset, void* caller)
|
||||
{
|
||||
}
|
||||
|
||||
void SoftAssetReferenceBase::OnAssetLoaded(Asset* asset, void* caller)
|
||||
{
|
||||
}
|
||||
|
||||
void SoftAssetReferenceBase::OnAssetUnloaded(Asset* asset, void* caller)
|
||||
{
|
||||
if (_asset != asset)
|
||||
return;
|
||||
_asset->RemoveReference(this);
|
||||
_asset = nullptr;
|
||||
_id = Guid::Empty;
|
||||
Changed();
|
||||
}
|
||||
|
||||
void SoftAssetReferenceBase::OnSet(Asset* asset)
|
||||
{
|
||||
if (_asset == asset)
|
||||
return;
|
||||
if (_asset)
|
||||
{
|
||||
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
|
||||
_asset->RemoveReference();
|
||||
}
|
||||
_asset->RemoveReference(this);
|
||||
_asset = asset;
|
||||
_id = asset ? asset->GetID() : Guid::Empty;
|
||||
if (asset)
|
||||
{
|
||||
asset->AddReference();
|
||||
asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
|
||||
}
|
||||
asset->AddReference(this);
|
||||
Changed();
|
||||
}
|
||||
|
||||
@@ -170,10 +201,7 @@ void SoftAssetReferenceBase::OnSet(const Guid& id)
|
||||
if (_id == id)
|
||||
return;
|
||||
if (_asset)
|
||||
{
|
||||
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
|
||||
_asset->RemoveReference();
|
||||
}
|
||||
_asset->RemoveReference(this);
|
||||
_asset = nullptr;
|
||||
_id = id;
|
||||
Changed();
|
||||
@@ -184,21 +212,7 @@ void SoftAssetReferenceBase::OnResolve(const ScriptingTypeHandle& type)
|
||||
ASSERT(!_asset);
|
||||
_asset = ::LoadAsset(_id, type);
|
||||
if (_asset)
|
||||
{
|
||||
_asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
|
||||
_asset->AddReference();
|
||||
}
|
||||
}
|
||||
|
||||
void SoftAssetReferenceBase::OnUnloaded(Asset* asset)
|
||||
{
|
||||
if (_asset != asset)
|
||||
return;
|
||||
_asset->RemoveReference();
|
||||
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
|
||||
_asset = nullptr;
|
||||
_id = Guid::Empty;
|
||||
Changed();
|
||||
_asset->AddReference(this);
|
||||
}
|
||||
|
||||
Asset::Asset(const SpawnParams& params, const AssetInfo* info)
|
||||
@@ -216,6 +230,39 @@ int32 Asset::GetReferencesCount() const
|
||||
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
|
||||
}
|
||||
|
||||
void Asset::AddReference()
|
||||
{
|
||||
Platform::InterlockedIncrement(&_refCount);
|
||||
}
|
||||
|
||||
void Asset::AddReference(IAssetReference* ref, bool week)
|
||||
{
|
||||
if (!week)
|
||||
Platform::InterlockedIncrement(&_refCount);
|
||||
if (ref)
|
||||
{
|
||||
//PROFILE_MEM(EngineDelegate); // Include references tracking memory within Delegate memory
|
||||
ScopeLock lock(_referencesLocker);
|
||||
_references.Add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
void Asset::RemoveReference()
|
||||
{
|
||||
Platform::InterlockedDecrement(&_refCount);
|
||||
}
|
||||
|
||||
void Asset::RemoveReference(IAssetReference* ref, bool week)
|
||||
{
|
||||
if (ref)
|
||||
{
|
||||
ScopeLock lock(_referencesLocker);
|
||||
_references.Remove(ref);
|
||||
}
|
||||
if (!week)
|
||||
Platform::InterlockedDecrement(&_refCount);
|
||||
}
|
||||
|
||||
String Asset::ToString() const
|
||||
{
|
||||
return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath());
|
||||
@@ -354,6 +401,7 @@ uint64 Asset::GetMemoryUsage() const
|
||||
if (Platform::AtomicRead(&_loadingTask))
|
||||
result += sizeof(ContentLoadTask);
|
||||
result += (OnLoaded.Capacity() + OnReloading.Capacity() + OnUnloaded.Capacity()) * sizeof(EventType::FunctionType);
|
||||
result += _references.Capacity() * sizeof(HashSet<IAssetReference*>::Bucket);
|
||||
Locker.Unlock();
|
||||
return result;
|
||||
}
|
||||
@@ -444,6 +492,9 @@ bool Asset::WaitForLoaded(double timeoutInMilliseconds) const
|
||||
}
|
||||
|
||||
PROFILE_CPU();
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
const StringView path(GetPath());
|
||||
ZoneText(*path, path.Length());
|
||||
|
||||
Content::WaitForTask(loadingTask, timeoutInMilliseconds);
|
||||
|
||||
@@ -528,6 +579,7 @@ ContentLoadTask* Asset::createLoadingTask()
|
||||
|
||||
void Asset::startLoading()
|
||||
{
|
||||
PROFILE_MEM(ContentAssets);
|
||||
ASSERT(!IsLoaded());
|
||||
ASSERT(Platform::AtomicRead(&_loadingTask) == 0);
|
||||
auto loadingTask = createLoadingTask();
|
||||
@@ -627,6 +679,9 @@ void Asset::onLoaded_MainThread()
|
||||
ASSERT(IsInMainThread());
|
||||
|
||||
// Send event
|
||||
ScopeLock lock(_referencesLocker);
|
||||
for (const auto& e : _references)
|
||||
e.Item->OnAssetLoaded(this, this);
|
||||
OnLoaded(this);
|
||||
}
|
||||
|
||||
@@ -640,6 +695,9 @@ void Asset::onUnload_MainThread()
|
||||
CancelStreaming();
|
||||
|
||||
// Send event
|
||||
ScopeLock lock(_referencesLocker);
|
||||
for (const auto& e : _references)
|
||||
e.Item->OnAssetUnloaded(this, this);
|
||||
OnUnloaded(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Threading/ConcurrentSystemLocker.h"
|
||||
#include "Config.h"
|
||||
#include "Types.h"
|
||||
|
||||
@@ -18,6 +19,20 @@
|
||||
public: \
|
||||
explicit type(const SpawnParams& params, const AssetInfo* info)
|
||||
|
||||
// Utility interface for objects that reference asset and want to get notified about asset reference changes.
|
||||
class FLAXENGINE_API IAssetReference
|
||||
{
|
||||
public:
|
||||
virtual ~IAssetReference() = default;
|
||||
|
||||
// Asset reference got changed.
|
||||
virtual void OnAssetChanged(Asset* asset, void* caller) = 0;
|
||||
// Asset got loaded.
|
||||
virtual void OnAssetLoaded(Asset* asset, void* caller) = 0;
|
||||
// Asset gets unloaded.
|
||||
virtual void OnAssetUnloaded(Asset* asset, void* caller) = 0;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Asset objects base class.
|
||||
/// </summary>
|
||||
@@ -48,6 +63,9 @@ protected:
|
||||
int8 _deleteFileOnUnload : 1; // Indicates that asset source file should be removed on asset unload
|
||||
int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved)
|
||||
|
||||
HashSet<IAssetReference*> _references;
|
||||
CriticalSection _referencesLocker; // TODO: convert into a single interlocked exchange for the current thread owning lock
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Asset"/> class.
|
||||
@@ -88,18 +106,22 @@ public:
|
||||
/// <summary>
|
||||
/// Adds reference to that asset.
|
||||
/// </summary>
|
||||
FORCE_INLINE void AddReference()
|
||||
{
|
||||
Platform::InterlockedIncrement(&_refCount);
|
||||
}
|
||||
void AddReference();
|
||||
|
||||
/// <summary>
|
||||
/// Adds reference to that asset.
|
||||
/// </summary>
|
||||
void AddReference(IAssetReference* ref, bool week = false);
|
||||
|
||||
/// <summary>
|
||||
/// Removes reference from that asset.
|
||||
/// </summary>
|
||||
FORCE_INLINE void RemoveReference()
|
||||
{
|
||||
Platform::InterlockedDecrement(&_refCount);
|
||||
}
|
||||
void RemoveReference();
|
||||
|
||||
/// <summary>
|
||||
/// Removes reference from that asset.
|
||||
/// </summary>
|
||||
void RemoveReference(IAssetReference* ref, bool week = false);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
/// <summary>
|
||||
/// Asset reference utility. Keeps reference to the linked asset object and handles load/unload events.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API AssetReferenceBase
|
||||
class FLAXENGINE_API AssetReferenceBase : public IAssetReference
|
||||
{
|
||||
protected:
|
||||
Asset* _asset = nullptr;
|
||||
IAssetReference* _owner = nullptr;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -36,6 +37,12 @@ public:
|
||||
/// </summary>
|
||||
AssetReferenceBase() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetReferenceBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
|
||||
AssetReferenceBase(IAssetReference* owner);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="AssetReferenceBase"/> class.
|
||||
/// </summary>
|
||||
@@ -63,10 +70,14 @@ public:
|
||||
/// </summary>
|
||||
String ToString() const;
|
||||
|
||||
public:
|
||||
// [IAssetReference]
|
||||
void OnAssetChanged(Asset* asset, void* caller) override;
|
||||
void OnAssetLoaded(Asset* asset, void* caller) override;
|
||||
void OnAssetUnloaded(Asset* asset, void* caller) override;
|
||||
|
||||
protected:
|
||||
void OnSet(Asset* asset);
|
||||
void OnLoaded(Asset* asset);
|
||||
void OnUnloaded(Asset* asset);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -87,6 +98,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetReference"/> class.
|
||||
/// </summary>
|
||||
explicit AssetReference(decltype(__nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetReference"/> class.
|
||||
/// </summary>
|
||||
@@ -96,6 +114,15 @@ public:
|
||||
OnSet((Asset*)asset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetReference"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">The reference owner to keep notified about asset changes.</param>
|
||||
explicit AssetReference(IAssetReference* owner)
|
||||
: AssetReferenceBase(owner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetReference"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Engine/Animations/Animations.h"
|
||||
#include "Engine/Animations/SceneAnimations/SceneAnimation.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#if USE_EDITOR
|
||||
@@ -598,6 +599,7 @@ void Animation::OnScriptingDispose()
|
||||
|
||||
Asset::LoadResult Animation::load()
|
||||
{
|
||||
PROFILE_MEM(AnimationsData);
|
||||
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
|
||||
|
||||
// Get stream with animations data
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Engine/Core/Types/DataContainer.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/Animations/Animations.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
@@ -25,6 +26,7 @@ AnimationGraph::AnimationGraph(const SpawnParams& params, const AssetInfo* info)
|
||||
|
||||
Asset::LoadResult AnimationGraph::load()
|
||||
{
|
||||
PROFILE_MEM(AnimationsData);
|
||||
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
|
||||
|
||||
// Get stream with graph data
|
||||
@@ -83,6 +85,7 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
|
||||
Log::ArgumentNullException();
|
||||
return true;
|
||||
}
|
||||
PROFILE_MEM(AnimationsData);
|
||||
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
|
||||
|
||||
// Create Graph data
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#endif
|
||||
#include "Engine/Animations/Animations.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
|
||||
@@ -20,6 +21,7 @@ AnimationGraphFunction::AnimationGraphFunction(const SpawnParams& params, const
|
||||
|
||||
Asset::LoadResult AnimationGraphFunction::load()
|
||||
{
|
||||
PROFILE_MEM(AnimationsData);
|
||||
ConcurrentSystemLocker::WriteScope systemScope(Animations::SystemLocker);
|
||||
|
||||
// Get graph data from chunk
|
||||
|
||||
@@ -165,9 +165,13 @@ Asset::LoadResult Material::load()
|
||||
MaterialGenerator generator;
|
||||
generator.Error.Bind(&OnGeneratorError);
|
||||
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION)
|
||||
{
|
||||
LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", name, _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Info, "Updating material \'{0}\'...", name);
|
||||
}
|
||||
|
||||
// Load or create material surface
|
||||
MaterialLayer* layer;
|
||||
@@ -410,16 +414,18 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
|
||||
// Prepare
|
||||
auto& info = _shaderHeader.Material.Info;
|
||||
const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable;
|
||||
const bool isOpaque = info.BlendMode == MaterialBlendMode::Opaque;
|
||||
const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage;
|
||||
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle;
|
||||
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && !isOpaque) || info.Domain == MaterialDomain::Particle;
|
||||
const bool useTess =
|
||||
info.TessellationMode != TessellationMethod::None &&
|
||||
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
|
||||
const bool useDistortion =
|
||||
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
|
||||
info.BlendMode != MaterialBlendMode::Opaque &&
|
||||
!isOpaque &&
|
||||
EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) &&
|
||||
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None;
|
||||
const MaterialShadingModel shadingModel = info.ShadingModel == MaterialShadingModel::CustomLit ? MaterialShadingModel::Unlit : info.ShadingModel;
|
||||
|
||||
// @formatter:off
|
||||
static const char* Numbers[] =
|
||||
@@ -431,7 +437,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
|
||||
// Setup shader macros
|
||||
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
|
||||
options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] });
|
||||
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)info.ShadingModel] });
|
||||
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)shadingModel] });
|
||||
options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] });
|
||||
options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
|
||||
options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] });
|
||||
@@ -488,7 +494,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
|
||||
options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] });
|
||||
options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] });
|
||||
options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] });
|
||||
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] });
|
||||
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && isOpaque ? 1 : 0] });
|
||||
options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] });
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Engine/Graphics/Models/MeshDeformation.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Tools/ModelTool/ModelTool.h"
|
||||
@@ -304,6 +305,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
|
||||
Log::ArgumentOutOfRangeException();
|
||||
return true;
|
||||
}
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
|
||||
// Dispose previous data and disable streaming (will start data uploading tasks manually)
|
||||
StopStreaming();
|
||||
@@ -343,6 +345,7 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
|
||||
|
||||
bool Model::LoadHeader(ReadStream& stream, byte& headerVersion)
|
||||
{
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
if (ModelBase::LoadHeader(stream, headerVersion))
|
||||
return true;
|
||||
|
||||
@@ -509,6 +512,7 @@ bool Model::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk
|
||||
|
||||
void Model::SetupMaterialSlots(int32 slotsCount)
|
||||
{
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
ModelBase::SetupMaterialSlots(slotsCount);
|
||||
|
||||
// Adjust meshes indices for slots
|
||||
@@ -584,6 +588,8 @@ int32 Model::GetAllocatedResidency() const
|
||||
|
||||
Asset::LoadResult Model::load()
|
||||
{
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
|
||||
// Get header chunk
|
||||
auto chunk0 = GetChunk(0);
|
||||
if (chunk0 == nullptr || chunk0->IsMissing())
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
#include "Engine/Content/WeakAssetReference.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Graphics/Config.h"
|
||||
#include "Engine/Graphics/Models/MeshBase.h"
|
||||
#include "Engine/Graphics/Models/MeshDeformation.h"
|
||||
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#if GPU_ENABLE_ASYNC_RESOURCES_CREATION
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#define STREAM_TASK_BASE ThreadPoolTask
|
||||
@@ -51,6 +53,7 @@ public:
|
||||
AssetReference<ModelBase> model = _model.Get();
|
||||
if (model == nullptr)
|
||||
return true;
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
|
||||
// Get data
|
||||
BytesContainer data;
|
||||
@@ -334,6 +337,8 @@ bool ModelBase::LoadHeader(ReadStream& stream, byte& headerVersion)
|
||||
|
||||
bool ModelBase::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
|
||||
{
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
|
||||
// Load descriptor
|
||||
static_assert(MODEL_MESH_VERSION == 2, "Update code");
|
||||
uint32 vertices, triangles;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/Content/Upgraders/SkeletonMaskUpgrader.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
|
||||
REGISTER_BINARY_ASSET_WITH_UPGRADER(SkeletonMask, "FlaxEngine.SkeletonMask", SkeletonMaskUpgrader, true);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h"
|
||||
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Graphics/Models/ModelData.h"
|
||||
@@ -458,6 +459,7 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
|
||||
Log::ArgumentOutOfRangeException();
|
||||
return true;
|
||||
}
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
|
||||
// Dispose previous data and disable streaming (will start data uploading tasks manually)
|
||||
StopStreaming();
|
||||
@@ -501,6 +503,7 @@ void BlendShape::LoadHeader(ReadStream& stream, byte headerVersion)
|
||||
|
||||
void BlendShape::Load(ReadStream& stream, byte meshVersion)
|
||||
{
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
UseNormals = stream.ReadBool();
|
||||
stream.ReadUint32(&MinVertexIndex);
|
||||
stream.ReadUint32(&MaxVertexIndex);
|
||||
@@ -531,6 +534,7 @@ void BlendShape::Save(WriteStream& stream) const
|
||||
|
||||
bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase* mesh, MeshData* dataIfReadOnly)
|
||||
{
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
if (ModelBase::LoadMesh(stream, meshVersion, mesh, dataIfReadOnly))
|
||||
return true;
|
||||
static_assert(MODEL_MESH_VERSION == 2, "Update code");
|
||||
@@ -560,6 +564,7 @@ bool SkinnedModel::LoadMesh(MemoryReadStream& stream, byte meshVersion, MeshBase
|
||||
|
||||
bool SkinnedModel::LoadHeader(ReadStream& stream, byte& headerVersion)
|
||||
{
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
if (ModelBase::LoadHeader(stream, headerVersion))
|
||||
return true;
|
||||
static_assert(MODEL_HEADER_VERSION == 2, "Update code");
|
||||
@@ -861,6 +866,7 @@ uint64 SkinnedModel::GetMemoryUsage() const
|
||||
|
||||
void SkinnedModel::SetupMaterialSlots(int32 slotsCount)
|
||||
{
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
ModelBase::SetupMaterialSlots(slotsCount);
|
||||
|
||||
// Adjust meshes indices for slots
|
||||
@@ -954,6 +960,7 @@ Asset::LoadResult SkinnedModel::load()
|
||||
if (chunk0 == nullptr || chunk0->IsMissing())
|
||||
return LoadResult::MissingDataChunk;
|
||||
MemoryReadStream headerStream(chunk0->Get(), chunk0->Size());
|
||||
PROFILE_MEM(GraphicsMeshes);
|
||||
|
||||
// Load asset data (anything but mesh contents that use streaming)
|
||||
byte headerVersion;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Serialization/JsonWriter.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#include "Engine/Threading/MainThreadTask.h"
|
||||
#include "Engine/Level/SceneObject.h"
|
||||
@@ -37,10 +38,12 @@ namespace
|
||||
|
||||
void PrintStack(LogType type)
|
||||
{
|
||||
#if LOG_ENABLE
|
||||
const String stack = VisualScripting::GetStackTrace();
|
||||
Log::Logger::Write(type, TEXT("Visual Script stack trace:"));
|
||||
Log::Logger::Write(type, stack);
|
||||
Log::Logger::Write(type, TEXT(""));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SerializeValue(const Variant& a, const Variant& b)
|
||||
@@ -1340,6 +1343,8 @@ bool VisualScript::Save(const StringView& path)
|
||||
|
||||
Asset::LoadResult VisualScript::load()
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
|
||||
// Build Visual Script typename that is based on asset id
|
||||
String typeName = _id.ToString();
|
||||
StringUtils::ConvertUTF162ANSI(typeName.Get(), _typenameChars, 32);
|
||||
@@ -1532,6 +1537,7 @@ Asset::LoadResult VisualScript::load()
|
||||
|
||||
void VisualScript::unload(bool isReloading)
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
#if USE_EDITOR
|
||||
if (isReloading)
|
||||
{
|
||||
@@ -1588,6 +1594,7 @@ AssetChunksFlag VisualScript::getChunksToPreload() const
|
||||
|
||||
void VisualScript::CacheScriptingType()
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
ScopeLock lock(VisualScriptingBinaryModule::Locker);
|
||||
auto& binaryModule = VisualScriptingModule;
|
||||
|
||||
@@ -1723,6 +1730,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
|
||||
VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex];
|
||||
|
||||
// Initialize instance data
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
ScopeLock lock(visualScript->Locker);
|
||||
auto& instanceParams = visualScript->_instances[object->GetID()].Params;
|
||||
instanceParams.Resize(visualScript->Graph.Parameters.Count());
|
||||
@@ -1747,6 +1755,8 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
|
||||
|
||||
void VisualScriptingBinaryModule::OnScriptsReloading()
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
|
||||
// Clear any cached types from that module across all loaded Visual Scripts
|
||||
for (auto& script : Scripts)
|
||||
{
|
||||
@@ -1795,6 +1805,7 @@ void VisualScriptingBinaryModule::OnScriptsReloading()
|
||||
|
||||
void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant> parameters, ScriptingTypeHandle eventType, StringView eventName)
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
if (object)
|
||||
{
|
||||
// Object event
|
||||
@@ -1900,9 +1911,13 @@ bool VisualScriptingBinaryModule::InvokeMethod(void* method, const Variant& inst
|
||||
if (!instanceObject || instanceObject->GetTypeHandle() != vsMethod->Script->GetScriptingType())
|
||||
{
|
||||
if (!instanceObject)
|
||||
{
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(vsMethod->Script->GetScriptTypeName()), String(vsMethod->Name), vsMethod->ParamNames.Count(), String(instanceObject->GetType().Fullname));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1952,6 +1967,7 @@ bool VisualScriptingBinaryModule::GetFieldValue(void* field, const Variant& inst
|
||||
|
||||
bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& instance, Variant& value)
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
const auto vsFiled = (VisualScript::Field*)field;
|
||||
const auto instanceObject = (ScriptingObject*)instance;
|
||||
if (!instanceObject)
|
||||
@@ -2038,6 +2054,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
|
||||
|
||||
void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier)
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
ASSERT(stream.IsObject());
|
||||
Locker.Lock();
|
||||
const auto asset = Scripts[object->GetTypeHandle().TypeIndex].Get();
|
||||
@@ -2161,6 +2178,7 @@ const Variant& VisualScript::GetScriptInstanceParameterValue(const StringView& n
|
||||
|
||||
void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, const Variant& value)
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
CHECK(instance);
|
||||
for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++)
|
||||
{
|
||||
@@ -2182,6 +2200,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip
|
||||
|
||||
void VisualScript::SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, Variant&& value)
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
CHECK(instance);
|
||||
for (int32 paramIndex = 0; paramIndex < Graph.Parameters.Count(); paramIndex++)
|
||||
{
|
||||
@@ -2379,6 +2398,7 @@ VisualScriptingBinaryModule* VisualScripting::GetBinaryModule()
|
||||
|
||||
Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters)
|
||||
{
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
|
||||
PROFILE_CPU_SRC_LOC(method->ProfilerData);
|
||||
|
||||
@@ -2419,6 +2439,7 @@ bool VisualScripting::Evaluate(VisualScript* script, ScriptingObject* instance,
|
||||
const auto box = node->GetBox(boxId);
|
||||
if (!box)
|
||||
return false;
|
||||
PROFILE_MEM(ScriptingVisual);
|
||||
|
||||
// Add to the calling stack
|
||||
ScopeContext scope;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
@@ -527,6 +528,7 @@ protected:
|
||||
auto storage = ref->Storage;
|
||||
auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName());
|
||||
ASSERT(factory);
|
||||
PROFILE_MEM(ContentAssets);
|
||||
|
||||
// Here we should open storage and extract AssetInitData
|
||||
// This would also allow to convert/upgrade data
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Level/Types.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#if USE_EDITOR
|
||||
@@ -117,6 +118,8 @@ ContentService ContentServiceInstance;
|
||||
|
||||
bool ContentService::Init()
|
||||
{
|
||||
PROFILE_MEM(Content);
|
||||
|
||||
// Load assets registry
|
||||
Cache.Init();
|
||||
|
||||
@@ -159,6 +162,7 @@ void ContentService::Update()
|
||||
void ContentService::LateUpdate()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Content);
|
||||
|
||||
// Check if need to perform an update of unloading assets
|
||||
const TimeSpan timeNow = Time::Update.UnscaledTime;
|
||||
@@ -324,6 +328,7 @@ String LoadingThread::ToString() const
|
||||
|
||||
int32 LoadingThread::Run()
|
||||
{
|
||||
PROFILE_MEM(Content);
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
// Initialize COM
|
||||
// TODO: maybe add sth to Thread::Create to indicate that thread will use COM stuff
|
||||
@@ -416,6 +421,7 @@ bool Content::GetAssetInfo(const Guid& id, AssetInfo& info)
|
||||
if (Cache.FindAsset(id, info))
|
||||
return true;
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Content);
|
||||
|
||||
// Locking injects some stalls but we need to make it safe (only one thread can pass though it at once)
|
||||
ScopeLock lock(WorkspaceDiscoveryLocker);
|
||||
@@ -465,6 +471,7 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
|
||||
if (!FileSystem::FileExists(path))
|
||||
return false;
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Content);
|
||||
|
||||
const auto extension = FileSystem::GetExtension(path).ToLower();
|
||||
|
||||
@@ -593,6 +600,7 @@ Asset* Content::LoadAsyncInternal(const StringView& internalPath, const MClass*
|
||||
|
||||
Asset* Content::LoadAsyncInternal(const StringView& internalPath, const ScriptingTypeHandle& type)
|
||||
{
|
||||
PROFILE_MEM(Content);
|
||||
#if USE_EDITOR
|
||||
const String path = Globals::EngineContentFolder / internalPath + ASSET_FILES_EXTENSION_WITH_DOT;
|
||||
if (!FileSystem::FileExists(path))
|
||||
@@ -635,6 +643,8 @@ Asset* Content::LoadAsync(const StringView& path, const MClass* type)
|
||||
|
||||
Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& type)
|
||||
{
|
||||
PROFILE_MEM(Content);
|
||||
|
||||
// Ensure path is in a valid format
|
||||
String pathNorm(path);
|
||||
ContentStorageManager::FormatPath(pathNorm);
|
||||
@@ -687,7 +697,6 @@ Asset* Content::GetAsset(const StringView& outputPath)
|
||||
{
|
||||
if (outputPath.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
ScopeLock lock(AssetsLocker);
|
||||
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
@@ -791,6 +800,23 @@ void Content::deleteFileSafety(const StringView& path, const Guid& id)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
|
||||
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||
|
||||
void* Content::GetAssetsInternal()
|
||||
{
|
||||
AssetsLocker.Lock();
|
||||
MArray* result = MCore::Array::New(Asset::TypeInitializer.GetClass(), Assets.Count());
|
||||
int32 i = 0;
|
||||
for (const auto& e : Assets)
|
||||
MCore::GC::WriteArrayRef(result, e.Value->GetOrCreateManagedInstance(), i++);
|
||||
AssetsLocker.Unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
bool Content::RenameAsset(const StringView& oldPath, const StringView& newPath)
|
||||
@@ -1023,6 +1049,7 @@ Asset* Content::CreateVirtualAsset(const MClass* type)
|
||||
Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Content);
|
||||
auto& assetType = type.GetType();
|
||||
|
||||
// Init mock asset info
|
||||
@@ -1045,7 +1072,9 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
|
||||
}
|
||||
|
||||
// Create asset object
|
||||
PROFILE_MEM_BEGIN(ContentAssets);
|
||||
auto asset = factory->NewVirtual(info);
|
||||
PROFILE_MEM_END();
|
||||
if (asset == nullptr)
|
||||
{
|
||||
LOG(Error, "Cannot create virtual asset object.");
|
||||
@@ -1054,7 +1083,9 @@ Asset* Content::CreateVirtualAsset(const ScriptingTypeHandle& type)
|
||||
asset->RegisterObject();
|
||||
|
||||
// Call initializer function
|
||||
PROFILE_MEM_BEGIN(ContentAssets);
|
||||
asset->InitAsVirtual();
|
||||
PROFILE_MEM_END();
|
||||
|
||||
// Register asset
|
||||
AssetsLocker.Lock();
|
||||
@@ -1097,6 +1128,8 @@ void Content::WaitForTask(ContentLoadTask* loadingTask, double timeoutInMillisec
|
||||
localQueue.Clear();
|
||||
}
|
||||
|
||||
PROFILE_CPU_NAMED("Inline");
|
||||
ZoneColor(0xffaaaaaa);
|
||||
thread->Run(tmp);
|
||||
}
|
||||
else
|
||||
@@ -1209,6 +1242,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
{
|
||||
if (!id.IsValid())
|
||||
return nullptr;
|
||||
PROFILE_MEM(Content);
|
||||
|
||||
// Check if asset has been already loaded
|
||||
Asset* result = nullptr;
|
||||
@@ -1277,7 +1311,9 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
}
|
||||
|
||||
// Create asset object
|
||||
PROFILE_MEM_BEGIN(ContentAssets);
|
||||
result = factory->New(assetInfo);
|
||||
PROFILE_MEM_END();
|
||||
if (result == nullptr)
|
||||
{
|
||||
LOG(Error, "Cannot create asset object. Info: {0}", assetInfo.ToString());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine.Interop;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -7,6 +8,33 @@ namespace FlaxEngine
|
||||
{
|
||||
partial class Content
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the assets (loaded or during load).
|
||||
/// </summary>
|
||||
public static Asset[] Assets
|
||||
{
|
||||
get
|
||||
{
|
||||
IntPtr ptr = Internal_GetAssetsInternal();
|
||||
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
|
||||
return NativeInterop.GCHandleArrayToManagedArray<Asset>(array);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assets (loaded or during load).
|
||||
/// </summary>
|
||||
/// <param name="buffer">Output buffer to fill with asset pointers. Can be provided by a user to avoid memory allocation. Buffer might be larger than actual list size. Use <paramref name="count"/> for actual item count.></param>
|
||||
/// <param name="count">Amount of valid items inside <paramref name="buffer"/>.</param>
|
||||
public static void GetAssets(ref Asset[] buffer, out int count)
|
||||
{
|
||||
count = 0;
|
||||
IntPtr ptr = Internal_GetAssetsInternal();
|
||||
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
|
||||
buffer = NativeInterop.GCHandleArrayToManagedArray<Asset>(array, buffer);
|
||||
count = buffer.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads asset to the Content Pool and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
|
||||
/// </summary>
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
/// Gets the assets (loaded or during load).
|
||||
/// </summary>
|
||||
/// <returns>The collection of assets.</returns>
|
||||
API_PROPERTY() static Array<Asset*, HeapAllocation> GetAssets();
|
||||
static Array<Asset*, HeapAllocation> GetAssets();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw dictionary of assets (loaded or during load).
|
||||
@@ -368,4 +368,9 @@ private:
|
||||
static void onAssetUnload(Asset* asset);
|
||||
static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId);
|
||||
static void deleteFileSafety(const StringView& path, const Guid& id);
|
||||
|
||||
// Internal bindings
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
API_FUNCTION(NoProxy) static void* GetAssetsInternal();
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Engine/Core/Cache.h"
|
||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MField.h"
|
||||
@@ -39,6 +40,7 @@ String JsonAssetBase::GetData() const
|
||||
if (Data == nullptr)
|
||||
return String::Empty;
|
||||
PROFILE_CPU_NAMED("JsonAsset.GetData");
|
||||
PROFILE_MEM(ContentAssets);
|
||||
rapidjson_flax::StringBuffer buffer;
|
||||
OnGetData(buffer);
|
||||
return String((const char*)buffer.GetString(), (int32)buffer.GetSize());
|
||||
@@ -49,6 +51,7 @@ void JsonAssetBase::SetData(const StringView& value)
|
||||
if (!IsLoaded())
|
||||
return;
|
||||
PROFILE_CPU_NAMED("JsonAsset.SetData");
|
||||
PROFILE_MEM(ContentAssets);
|
||||
const StringAnsi dataJson(value);
|
||||
ScopeLock lock(Locker);
|
||||
const StringView dataTypeName = DataTypeName;
|
||||
@@ -60,6 +63,7 @@ void JsonAssetBase::SetData(const StringView& value)
|
||||
|
||||
bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& dataJson)
|
||||
{
|
||||
PROFILE_MEM(ContentAssets);
|
||||
unload(true);
|
||||
DataTypeName = dataTypeName;
|
||||
DataEngineBuild = FLAXENGINE_VERSION_BUILD;
|
||||
@@ -239,6 +243,7 @@ Asset::LoadResult JsonAssetBase::loadAsset()
|
||||
{
|
||||
if (IsVirtual() || _isVirtualDocument)
|
||||
return LoadResult::Ok;
|
||||
PROFILE_MEM(ContentAssets);
|
||||
|
||||
// Load data (raw json file in editor, cooked asset in build game)
|
||||
#if USE_EDITOR
|
||||
@@ -305,6 +310,7 @@ Asset::LoadResult JsonAssetBase::loadAsset()
|
||||
|
||||
void JsonAssetBase::unload(bool isReloading)
|
||||
{
|
||||
PROFILE_MEM(ContentAssets);
|
||||
ISerializable::SerializeDocument tmp;
|
||||
Document.Swap(tmp);
|
||||
Data = nullptr;
|
||||
@@ -453,6 +459,7 @@ bool JsonAsset::CreateInstance()
|
||||
ScopeLock lock(Locker);
|
||||
if (Instance)
|
||||
return false;
|
||||
PROFILE_MEM(ContentAssets);
|
||||
|
||||
// Try to scripting type for this data
|
||||
const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length());
|
||||
|
||||
@@ -19,6 +19,15 @@ API_STRUCT(NoDefault, Template, MarshalAs=JsonAsset*) struct JsonAssetReference
|
||||
OnSet(asset);
|
||||
}
|
||||
|
||||
explicit JsonAssetReference(decltype(__nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
explicit JsonAssetReference(IAssetReference* owner)
|
||||
: AssetReference<JsonAsset>(owner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type.
|
||||
/// </summary>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Content/WeakAssetReference.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
|
||||
/// <summary>
|
||||
/// Asset loading task object.
|
||||
@@ -44,6 +45,7 @@ protected:
|
||||
Result run() override
|
||||
{
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(ContentAssets);
|
||||
|
||||
// Keep valid ref to the asset
|
||||
AssetReference<::Asset> ref = Asset.Get();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/// <summary>
|
||||
/// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it).
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SoftAssetReferenceBase
|
||||
class FLAXENGINE_API SoftAssetReferenceBase : public IAssetReference
|
||||
{
|
||||
protected:
|
||||
Asset* _asset = nullptr;
|
||||
@@ -46,11 +46,16 @@ public:
|
||||
/// </summary>
|
||||
String ToString() const;
|
||||
|
||||
public:
|
||||
// [IAssetReference]
|
||||
void OnAssetChanged(Asset* asset, void* caller) override;
|
||||
void OnAssetLoaded(Asset* asset, void* caller) override;
|
||||
void OnAssetUnloaded(Asset* asset, void* caller) override;
|
||||
|
||||
protected:
|
||||
void OnSet(Asset* asset);
|
||||
void OnSet(const Guid& id);
|
||||
void OnResolve(const ScriptingTypeHandle& type);
|
||||
void OnUnloaded(Asset* asset);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -71,6 +76,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
|
||||
/// </summary>
|
||||
explicit SoftAssetReference(decltype(__nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -182,10 +182,5 @@ public:
|
||||
/// Clones this chunk data (doesn't copy location in file).
|
||||
/// </summary>
|
||||
/// <returns>The cloned chunk.</returns>
|
||||
FlaxChunk* Clone() const
|
||||
{
|
||||
auto chunk = New<FlaxChunk>();
|
||||
chunk->Data.Copy(Data);
|
||||
return chunk;
|
||||
}
|
||||
FlaxChunk* Clone() const;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Serialization/FileWriteStream.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
@@ -63,6 +64,14 @@ void FlaxChunk::RegisterUsage()
|
||||
LastAccessTime = Platform::GetTimeSeconds();
|
||||
}
|
||||
|
||||
FlaxChunk* FlaxChunk::Clone() const
|
||||
{
|
||||
PROFILE_MEM(ContentFiles);
|
||||
auto chunk = New<FlaxChunk>();
|
||||
chunk->Data.Copy(Data);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
const int32 FlaxStorage::MagicCode = 1180124739;
|
||||
|
||||
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
|
||||
@@ -281,19 +290,12 @@ uint32 FlaxStorage::GetMemoryUsage() const
|
||||
|
||||
bool FlaxStorage::Load()
|
||||
{
|
||||
// Check if was already loaded
|
||||
if (IsLoaded())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent loading by more than one thread
|
||||
PROFILE_MEM(ContentFiles);
|
||||
ScopeLock lock(_loadLocker);
|
||||
if (IsLoaded())
|
||||
{
|
||||
// Other thread loaded it
|
||||
return false;
|
||||
}
|
||||
ASSERT(GetEntriesCount() == 0);
|
||||
|
||||
// Open file
|
||||
@@ -693,6 +695,7 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data)
|
||||
|
||||
bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
|
||||
{
|
||||
PROFILE_MEM(ContentFiles);
|
||||
ASSERT(IsLoaded());
|
||||
ASSERT(chunk != nullptr && _chunks.Contains(chunk));
|
||||
|
||||
@@ -866,6 +869,7 @@ FlaxChunk* FlaxStorage::AllocateChunk()
|
||||
{
|
||||
if (AllowDataModifications())
|
||||
{
|
||||
PROFILE_MEM(ContentFiles);
|
||||
auto chunk = New<FlaxChunk>();
|
||||
_chunks.Add(chunk);
|
||||
return chunk;
|
||||
@@ -1125,6 +1129,7 @@ bool FlaxStorage::Save(const AssetInitData& data, bool silentMode)
|
||||
|
||||
bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data)
|
||||
{
|
||||
PROFILE_MEM(ContentFiles);
|
||||
ASSERT(IsLoaded());
|
||||
|
||||
auto lock = Lock();
|
||||
@@ -1396,6 +1401,8 @@ FileReadStream* FlaxStorage::OpenFile()
|
||||
auto& stream = _file.Get();
|
||||
if (stream == nullptr)
|
||||
{
|
||||
PROFILE_MEM(ContentFiles);
|
||||
|
||||
// Open file
|
||||
auto file = File::Open(_path, FileMode::OpenExisting, FileAccess::Read, FileShare::Read);
|
||||
if (file == nullptr)
|
||||
@@ -1418,6 +1425,7 @@ bool FlaxStorage::CloseFileHandles()
|
||||
return false;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(ContentFiles);
|
||||
|
||||
// Note: this is usually called by the content manager when this file is not used or on exit
|
||||
// In those situations all the async tasks using this storage should be cancelled externally
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/// <summary>
|
||||
/// Asset reference utility that doesn't add reference to that asset. Handles asset unload event.
|
||||
/// </summary>
|
||||
API_CLASS(InBuild) class WeakAssetReferenceBase
|
||||
API_CLASS(InBuild) class WeakAssetReferenceBase : public IAssetReference
|
||||
{
|
||||
public:
|
||||
typedef Delegate<> EventType;
|
||||
@@ -56,9 +56,14 @@ public:
|
||||
/// </summary>
|
||||
String ToString() const;
|
||||
|
||||
public:
|
||||
// [IAssetReference]
|
||||
void OnAssetChanged(Asset* asset, void* caller) override;
|
||||
void OnAssetLoaded(Asset* asset, void* caller) override;
|
||||
void OnAssetUnloaded(Asset* asset, void* caller) override;
|
||||
|
||||
protected:
|
||||
void OnSet(Asset* asset);
|
||||
void OnUnloaded(Asset* asset);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -72,7 +77,13 @@ public:
|
||||
/// Initializes a new instance of the <see cref="WeakAssetReference"/> class.
|
||||
/// </summary>
|
||||
WeakAssetReference()
|
||||
: WeakAssetReferenceBase()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeakAssetReference"/> class.
|
||||
/// </summary>
|
||||
explicit WeakAssetReference(decltype(__nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -81,7 +92,6 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="asset">The asset to set.</param>
|
||||
WeakAssetReference(T* asset)
|
||||
: WeakAssetReferenceBase()
|
||||
{
|
||||
OnSet(asset);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "ImportTexture.h"
|
||||
#include "ImportModel.h"
|
||||
@@ -151,6 +152,7 @@ bool CreateAssetContext::AllocateChunk(int32 index)
|
||||
}
|
||||
|
||||
// Create new chunk
|
||||
PROFILE_MEM(ContentFiles);
|
||||
Data.Header.Chunks[index] = New<FlaxChunk>();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "Cache.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
|
||||
CollectionPoolCache<ISerializeModifier, Cache::ISerializeModifierClearCallback> Cache::ISerializeModifier;
|
||||
Cache::ISerializeModifierCache Cache::ISerializeModifier;
|
||||
|
||||
void Cache::ISerializeModifierClearCallback(::ISerializeModifier* obj)
|
||||
{
|
||||
|
||||
@@ -15,11 +15,12 @@ public:
|
||||
static void ISerializeModifierClearCallback(ISerializeModifier* obj);
|
||||
|
||||
public:
|
||||
typedef CollectionPoolCache<ISerializeModifier, ISerializeModifierClearCallback> ISerializeModifierCache;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ISerializeModifier lookup cache. Safe allocation, per thread, uses caching.
|
||||
/// </summary>
|
||||
static CollectionPoolCache<ISerializeModifier, ISerializeModifierClearCallback> ISerializeModifier;
|
||||
static ISerializeModifierCache ISerializeModifier;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ API_CLASS(InBuild) class Array
|
||||
public:
|
||||
using ItemType = T;
|
||||
using AllocationData = typename AllocationType::template Data<T>;
|
||||
using AllocationTag = typename AllocationType::Tag;
|
||||
|
||||
private:
|
||||
int32 _count;
|
||||
@@ -36,6 +37,17 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an empty <see cref="Array"/> without reserving any space.
|
||||
/// </summary>
|
||||
/// <param name="tag">The custom allocation tag.</param>
|
||||
Array(AllocationTag tag)
|
||||
: _count(0)
|
||||
, _capacity(0)
|
||||
, _allocation(tag)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes <see cref="Array"/> by reserving space.
|
||||
/// </summary>
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
#endif
|
||||
|
||||
// Enable logging service (saving log to file, can be disabled using -nolog command line)
|
||||
#ifndef LOG_ENABLE
|
||||
#define LOG_ENABLE 1
|
||||
#endif
|
||||
|
||||
// Enable crash reporting service (stack trace and crash dump collecting)
|
||||
#define CRASH_LOG_ENABLE (!BUILD_RELEASE)
|
||||
|
||||
// Enable/disable assertion
|
||||
#ifndef ENABLE_ASSERTION
|
||||
#define ENABLE_ASSERTION (!BUILD_RELEASE)
|
||||
#endif
|
||||
|
||||
// Enable/disable assertion for Engine low layers
|
||||
#define ENABLE_ASSERTION_LOW_LAYERS ENABLE_ASSERTION && (BUILD_DEBUG || FLAX_TESTS)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user