diff --git a/Content/Shaders/BitonicSort.flax b/Content/Shaders/BitonicSort.flax
index 1d5b8a581..c9dd81dc9 100644
--- a/Content/Shaders/BitonicSort.flax
+++ b/Content/Shaders/BitonicSort.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f46a61cf8d5183230176e661a51208bfeece16cc7238655f406288ff448af64e
-size 6721
+oid sha256:0c780dc1881ef96dece237bde3e87fca3c9e4e542d89ebde7c9308f00f81c6a9
+size 6808
diff --git a/Flax.flaxproj b/Flax.flaxproj
index cc643346f..681c1dc55 100644
--- a/Flax.flaxproj
+++ b/Flax.flaxproj
@@ -3,7 +3,7 @@
"Version": {
"Major": 1,
"Minor": 1,
- "Build": 6217
+ "Build": 6218
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.",
diff --git a/Flax.sln.DotSettings b/Flax.sln.DotSettings
index b940e9c03..c91a48bfe 100644
--- a/Flax.sln.DotSettings
+++ b/Flax.sln.DotSettings
@@ -234,6 +234,7 @@
Deprecated
(?<=\W|^)(?<TAG>\[Deprecated)(\W|$)(.*)
Normal
+ True
True
True
True
diff --git a/Source/Editor/Analytics/EditorAnalytics.cpp b/Source/Editor/Analytics/EditorAnalytics.cpp
index 3f067719e..c2a293c15 100644
--- a/Source/Editor/Analytics/EditorAnalytics.cpp
+++ b/Source/Editor/Analytics/EditorAnalytics.cpp
@@ -10,6 +10,7 @@
#include "Editor/Editor.h"
#include "Editor/ProjectInfo.h"
#include "Engine/Engine/EngineService.h"
+#include "Engine/Engine/Globals.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Utilities/StringConverter.h"
#include "FlaxEngine.Gen.h"
diff --git a/Source/Editor/Content/Create/SettingsCreateEntry.cs b/Source/Editor/Content/Create/SettingsCreateEntry.cs
index 369cc01e5..974de76cc 100644
--- a/Source/Editor/Content/Create/SettingsCreateEntry.cs
+++ b/Source/Editor/Content/Create/SettingsCreateEntry.cs
@@ -53,6 +53,11 @@ namespace FlaxEditor.Content.Create
///
NavigationSettings,
+ ///
+ /// The localization settings.
+ ///
+ LocalizationSettings,
+
///
/// The build settings.
///
@@ -92,6 +97,11 @@ namespace FlaxEditor.Content.Create
/// The Android settings
///
AndroidPlatformSettings,
+
+ ///
+ /// The Switch settings
+ ///
+ SwitchPlatformSettings,
}
private static readonly Type[] _types =
@@ -103,6 +113,7 @@ namespace FlaxEditor.Content.Create
typeof(PhysicsSettings),
typeof(GraphicsSettings),
typeof(NavigationSettings),
+ typeof(LocalizationSettings),
typeof(BuildSettings),
typeof(InputSettings),
typeof(WindowsPlatformSettings),
@@ -111,6 +122,7 @@ namespace FlaxEditor.Content.Create
TypeUtils.GetManagedType(GameSettings.PS4PlatformSettingsTypename),
TypeUtils.GetManagedType(GameSettings.XboxScarlettPlatformSettingsTypename),
typeof(AndroidPlatformSettings),
+ TypeUtils.GetManagedType(GameSettings.SwitchPlatformSettingsTypename),
};
///
diff --git a/Source/Editor/Content/Import/ImportFileEntry.cs b/Source/Editor/Content/Import/ImportFileEntry.cs
index 80cab94d3..5d21d3726 100644
--- a/Source/Editor/Content/Import/ImportFileEntry.cs
+++ b/Source/Editor/Content/Import/ImportFileEntry.cs
@@ -116,8 +116,7 @@ namespace FlaxEditor.Content.Import
if (FileTypes.TryGetValue(extension, out ImportFileEntryHandler createDelegate))
return createDelegate(ref request);
- // Use default type
- return request.IsBinaryAsset ? new AssetImportEntry(ref request) : new ImportFileEntry(ref request);
+ return request.IsInBuilt ? new AssetImportEntry(ref request) : new ImportFileEntry(ref request);
}
internal static void RegisterDefaultTypes()
diff --git a/Source/Editor/Content/Import/Request.cs b/Source/Editor/Content/Import/Request.cs
index b9da30afb..d38b0bfd0 100644
--- a/Source/Editor/Content/Import/Request.cs
+++ b/Source/Editor/Content/Import/Request.cs
@@ -21,9 +21,9 @@ namespace FlaxEditor.Content.Import
public string OutputPath;
///
- /// Flag set to true for binary assets handled by the engine internally.
+ /// Flag set to true for the assets handled by the engine internally.
///
- public bool IsBinaryAsset;
+ public bool IsInBuilt;
///
/// Flag used to skip showing import settings dialog to used. Can be used for importing assets from code by plugins.
diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs
index aab0cb19f..04311644b 100644
--- a/Source/Editor/Content/Items/VisualScriptItem.cs
+++ b/Source/Editor/Content/Items/VisualScriptItem.cs
@@ -75,6 +75,7 @@ namespace FlaxEditor.Content
}
}
+ ///
public int MetadataToken => 0;
///
diff --git a/Source/Editor/Content/PreviewsCache.cpp b/Source/Editor/Content/PreviewsCache.cpp
index c51e8cabb..c89b00004 100644
--- a/Source/Editor/Content/PreviewsCache.cpp
+++ b/Source/Editor/Content/PreviewsCache.cpp
@@ -76,7 +76,7 @@ void PreviewsCache::FlushTask::OnEnd()
ThreadPoolTask::OnEnd();
}
-REGISTER_BINARY_ASSET(PreviewsCache, "FlaxEditor.PreviewsCache", ::New(), false);
+REGISTER_BINARY_ASSET_WITH_UPGRADER(PreviewsCache, "FlaxEditor.PreviewsCache", TextureAssetUpgrader, false);
PreviewsCache::PreviewsCache(const SpawnParams& params, const AssetInfo* info)
: SpriteAtlas(params, info)
diff --git a/Source/Editor/Content/Proxy/JsonAssetProxy.cs b/Source/Editor/Content/Proxy/JsonAssetProxy.cs
index 25155d39d..41cc06ee1 100644
--- a/Source/Editor/Content/Proxy/JsonAssetProxy.cs
+++ b/Source/Editor/Content/Proxy/JsonAssetProxy.cs
@@ -2,7 +2,6 @@
using System;
using FlaxEditor.Content.Create;
-using FlaxEditor.Content.Settings;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.Windows;
@@ -45,18 +44,12 @@ namespace FlaxEditor.Content
///
public override bool IsProxyFor(ContentItem item)
{
- return item is JsonAssetItem;
+ return item is JsonAssetItem json && json.TypeName == TypeName;
}
///
public override Color AccentColor => Color.FromRGB(0xd14f67);
- ///
- public override bool AcceptsAsset(string typeName, string path)
- {
- return typeName == TypeName && base.AcceptsAsset(typeName, path);
- }
-
///
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
{
@@ -143,6 +136,12 @@ namespace FlaxEditor.Content
return path.EndsWith(FileExtension, StringComparison.OrdinalIgnoreCase);
}
+ ///
+ public override bool IsProxyFor(ContentItem item)
+ {
+ return item is JsonAssetItem;
+ }
+
///
public override bool CanCreate(ContentFolder targetLocation)
{
diff --git a/Source/Editor/Content/Proxy/LocalizedStringTableProxy.cs b/Source/Editor/Content/Proxy/LocalizedStringTableProxy.cs
new file mode 100644
index 000000000..2e15f9543
--- /dev/null
+++ b/Source/Editor/Content/Proxy/LocalizedStringTableProxy.cs
@@ -0,0 +1,24 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using FlaxEditor.Windows;
+using FlaxEditor.Windows.Assets;
+using FlaxEngine;
+
+namespace FlaxEditor.Content
+{
+ ///
+ /// proxy.
+ ///
+ ///
+ public class LocalizedStringTableProxy : JsonAssetProxy
+ {
+ ///
+ public override EditorWindow Open(Editor editor, ContentItem item)
+ {
+ return new LocalizedStringTableWindow(editor, (JsonAssetItem)item);
+ }
+
+ ///
+ public override string TypeName => "FlaxEngine.LocalizedStringTable";
+ }
+}
diff --git a/Source/Editor/Cooker/CookingData.h b/Source/Editor/Cooker/CookingData.h
index 56b15af23..77774a9df 100644
--- a/Source/Editor/Cooker/CookingData.h
+++ b/Source/Editor/Cooker/CookingData.h
@@ -7,6 +7,7 @@
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/HashSet.h"
#include "Engine/Core/Collections/Dictionary.h"
+#include "Engine/Core/Types/Guid.h"
class GameCooker;
class PlatformTools;
@@ -87,34 +88,14 @@ API_ENUM() enum class BuildPlatform
///
API_ENUM(Attributes="EditorDisplay(null, \"Android ARM64 (arm64-v8a)\")")
AndroidARM64 = 9,
+
+ ///
+ /// Switch.
+ ///
+ Switch = 10,
};
-inline const Char* ToString(const BuildPlatform platform)
-{
- switch (platform)
- {
- case BuildPlatform::Windows32:
- return TEXT("Windows x86");
- case BuildPlatform::Windows64:
- return TEXT("Windows x64");
- case BuildPlatform::UWPx86:
- return TEXT("Windows Store x86");
- case BuildPlatform::UWPx64:
- return TEXT("Windows Store x64");
- case BuildPlatform::XboxOne:
- return TEXT("Xbox One");
- case BuildPlatform::LinuxX64:
- return TEXT("Linux x64");
- case BuildPlatform::PS4:
- return TEXT("PlayStation 4");
- case BuildPlatform::XboxScarlett:
- return TEXT("Xbox Scarlett");
- case BuildPlatform::AndroidARM64:
- return TEXT("Android ARM64");
- default:
- return TEXT("?");
- }
-}
+extern FLAXENGINE_API const Char* ToString(const BuildPlatform platform);
///
/// Game build configuration modes.
@@ -137,20 +118,7 @@ API_ENUM() enum class BuildConfiguration
Release = 2,
};
-inline const Char* ToString(const BuildConfiguration configuration)
-{
- switch (configuration)
- {
- case BuildConfiguration::Debug:
- return TEXT("Debug");
- case BuildConfiguration::Development:
- return TEXT("Development");
- case BuildConfiguration::Release:
- return TEXT("Release");
- default:
- return TEXT("?");
- }
-}
+extern FLAXENGINE_API const Char* ToString(const BuildConfiguration configuration);
#define BUILD_STEP_CANCEL_CHECK if (GameCooker::IsCancelRequested()) return true
@@ -185,9 +153,14 @@ struct FLAXENGINE_API CookingData
String OriginalOutputPath;
///
- /// The output path.
+ /// The output path for data files (Content, Mono, etc.).
///
- String OutputPath;
+ String DataOutputPath;
+
+ ///
+ /// The output path for binaries (executable and code libraries).
+ ///
+ String CodeOutputPath;
///
/// The platform tools.
diff --git a/Source/Editor/Cooker/GameCooker.cpp b/Source/Editor/Cooker/GameCooker.cpp
index 9eca81eba..f64045ff5 100644
--- a/Source/Editor/Cooker/GameCooker.cpp
+++ b/Source/Editor/Cooker/GameCooker.cpp
@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Content/Content.h"
#include "Engine/Engine/EngineService.h"
+#include "Engine/Engine/Globals.h"
#include "Engine/Threading/ThreadSpawner.h"
#include "Engine/Platform/FileSystem.h"
#include "Steps/ValidateStep.h"
@@ -39,7 +40,6 @@
#endif
#if PLATFORM_TOOLS_PS4
#include "Platforms/PS4/Editor/PlatformTools/PS4PlatformTools.h"
-#include "Platforms/PS4/Engine/Platform/PS4PlatformSettings.h"
#endif
#if PLATFORM_TOOLS_XBOX_SCARLETT
#include "Platforms/XboxScarlett/Editor/PlatformTools/XboxScarlettPlatformTools.h"
@@ -47,6 +47,9 @@
#if PLATFORM_TOOLS_ANDROID
#include "Platform/Android/AndroidPlatformTools.h"
#endif
+#if PLATFORM_TOOLS_SWITCH
+#include "Platforms/Switch/Editor/PlatformTools/SwitchPlatformTools.h"
+#endif
namespace GameCookerImpl
{
@@ -89,6 +92,50 @@ using namespace GameCookerImpl;
Delegate GameCooker::OnEvent;
Delegate GameCooker::OnProgress;
+const Char* ToString(const BuildPlatform platform)
+{
+ switch (platform)
+ {
+ case BuildPlatform::Windows32:
+ return TEXT("Windows x86");
+ case BuildPlatform::Windows64:
+ return TEXT("Windows x64");
+ case BuildPlatform::UWPx86:
+ return TEXT("Windows Store x86");
+ case BuildPlatform::UWPx64:
+ return TEXT("Windows Store x64");
+ case BuildPlatform::XboxOne:
+ return TEXT("Xbox One");
+ case BuildPlatform::LinuxX64:
+ return TEXT("Linux x64");
+ case BuildPlatform::PS4:
+ return TEXT("PlayStation 4");
+ case BuildPlatform::XboxScarlett:
+ return TEXT("Xbox Scarlett");
+ case BuildPlatform::AndroidARM64:
+ return TEXT("Android ARM64");
+ case BuildPlatform::Switch:
+ return TEXT("Switch");
+ default:
+ return TEXT("?");
+ }
+}
+
+const Char* ToString(const BuildConfiguration configuration)
+{
+ switch (configuration)
+ {
+ case BuildConfiguration::Debug:
+ return TEXT("Debug");
+ case BuildConfiguration::Development:
+ return TEXT("Development");
+ case BuildConfiguration::Release:
+ return TEXT("Release");
+ default:
+ return TEXT("?");
+ }
+}
+
bool CookingData::AssetTypeStatistics::operator<(const AssetTypeStatistics& other) const
{
if (ContentSize != other.ContentSize)
@@ -250,6 +297,11 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform)
case BuildPlatform::AndroidARM64:
result = New(ArchitectureType::ARM64);
break;
+#endif
+#if PLATFORM_TOOLS_SWITCH
+ case BuildPlatform::Switch:
+ result = New();
+ break;
#endif
}
Tools.Add(platform, result);
@@ -282,9 +334,10 @@ void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
data.Configuration = configuration;
data.Options = options;
data.CustomDefines = customDefines;
- data.OutputPath = outputPath;
- FileSystem::NormalizePath(data.OutputPath);
- data.OutputPath = data.OriginalOutputPath = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, data.OutputPath);
+ data.OriginalOutputPath = outputPath;
+ FileSystem::NormalizePath(data.OriginalOutputPath);
+ data.OriginalOutputPath = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, data.OriginalOutputPath);
+ data.CodeOutputPath = data.DataOutputPath = data.OriginalOutputPath;
data.CacheDirectory = Globals::ProjectCacheFolder / TEXT("Cooker") / tools->GetName();
if (!FileSystem::DirectoryExists(data.CacheDirectory))
{
@@ -367,7 +420,7 @@ bool GameCookerImpl::Build()
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));
- LOG(Info, "Output Path: {0}", data.OutputPath);
+ LOG(Info, "Output Path: {0}", data.OriginalOutputPath);
// Late init feature
if (Steps.IsEmpty())
diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp
index fcc53f26a..3987fcb4a 100644
--- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp
+++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp
@@ -104,7 +104,8 @@ PixelFormat AndroidPlatformTools::GetTextureFormat(CookingData& data, TextureBas
void AndroidPlatformTools::OnBuildStarted(CookingData& data)
{
// Adjust the cooking output folder to be located inside the Gradle assets directory
- data.OutputPath /= TEXT("app/assets");
+ data.DataOutputPath /= TEXT("app/assets");
+ data.CodeOutputPath /= TEXT("app/assets");
PlatformTools::OnBuildStarted(data);
}
@@ -114,7 +115,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
const auto gameSettings = GameSettings::Get();
const auto platformSettings = AndroidPlatformSettings::Get();
const auto platformDataPath = data.GetPlatformBinariesRoot();
- const auto assetsPath = data.OutputPath;
+ const auto assetsPath = data.DataOutputPath;
const auto jniLibsPath = data.OriginalOutputPath / TEXT("app/jniLibs");
const auto projectVersion = Editor::Project->Version.ToString();
const Char* abi;
diff --git a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp
index 69521e172..57562f537 100644
--- a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp
+++ b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp
@@ -38,7 +38,7 @@ bool LinuxPlatformTools::OnDeployBinaries(CookingData& data)
{
const auto gameSettings = GameSettings::Get();
const auto platformSettings = LinuxPlatformSettings::Get();
- const auto outputPath = data.OutputPath;
+ const auto outputPath = data.DataOutputPath;
// Copy binaries
{
diff --git a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp
index c8f9e45a7..e6516bf67 100644
--- a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp
+++ b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp
@@ -25,7 +25,7 @@ bool UWPPlatformTools::OnScriptsStepDone(CookingData& data)
{
// Override Newtonsoft.Json.dll for some platforms (that don't support runtime code generation)
const String customBinPath = data.GetPlatformBinariesRoot() / TEXT("Newtonsoft.Json.dll");
- const String assembliesPath = data.OutputPath;
+ const String assembliesPath = data.CodeOutputPath;
if (FileSystem::CopyFile(assembliesPath / TEXT("Newtonsoft.Json.dll"), customBinPath))
{
data.Error(TEXT("Failed to copy deploy custom assembly."));
@@ -43,7 +43,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
const auto uwpDataPath = platformDataPath / (isXboxOne ? TEXT("XboxOne") : TEXT("UWP")) / TEXT("Binaries");
const auto gameSettings = GameSettings::Get();
const auto platformSettings = UWPPlatformSettings::Get();
- Array fileTemplate;
+ StringAnsi fileTemplate;
// Copy binaries
const auto binPath = data.GetGameBinariesPath();
@@ -64,7 +64,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
return true;
}
- if (FileSystem::CopyFile(data.OutputPath / StringUtils::GetFileName(files[i]), files[i]))
+ if (FileSystem::CopyFile(data.DataOutputPath / StringUtils::GetFileName(files[i]), files[i]))
{
data.Error(TEXT("Failed to setup output directory."));
return true;
@@ -92,7 +92,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
// Prepare certificate
const auto srcCertificatePath = Globals::ProjectFolder / platformSettings->CertificateLocation;
- const auto dstCertificatePath = data.OutputPath / TEXT("WSACertificate.pfx");
+ const auto dstCertificatePath = data.DataOutputPath / TEXT("WSACertificate.pfx");
if (platformSettings->CertificateLocation.HasChars() && FileSystem::FileExists(srcCertificatePath))
{
// Use cert from settings
@@ -115,7 +115,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
// Copy assets
- const auto dstAssetsPath = data.OutputPath / TEXT("Assets");
+ const auto dstAssetsPath = data.DataOutputPath / TEXT("Assets");
const auto srcAssetsPath = uwpDataPath / TEXT("Assets");
if (!FileSystem::DirectoryExists(dstAssetsPath))
{
@@ -125,7 +125,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
return true;
}
}
- const auto dstPropertiesPath = data.OutputPath / TEXT("Properties");
+ const auto dstPropertiesPath = data.DataOutputPath / TEXT("Properties");
if (!FileSystem::DirectoryExists(dstPropertiesPath))
{
if (FileSystem::CreateDirectory(dstPropertiesPath))
@@ -149,12 +149,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (!FileSystem::FileExists(dstAssemblyInfoPath))
{
// Get template
- if (File::ReadAllBytes(srcAssemblyInfoPath, fileTemplate))
+ if (File::ReadAllText(srcAssemblyInfoPath, fileTemplate))
{
data.Error(TEXT("Failed to load AssemblyInfo.cs template."));
return true;
}
- fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstAssemblyInfoPath);
@@ -163,7 +162,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
{
auto now = DateTime::Now();
file->WriteTextFormatted(
- (char*)fileTemplate.Get()
+ fileTemplate.Get()
, gameSettings->ProductName.ToStringAnsi()
, gameSettings->CompanyName.ToStringAnsi()
, now.GetYear()
@@ -177,17 +176,16 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
return true;
}
}
- const auto dstAppPath = data.OutputPath / TEXT("App.cs");
+ const auto dstAppPath = data.DataOutputPath / TEXT("App.cs");
const auto srcAppPath = uwpDataPath / TEXT("App.cs");
if (!FileSystem::FileExists(dstAppPath))
{
// Get template
- if (File::ReadAllBytes(srcAppPath, fileTemplate))
+ if (File::ReadAllText(srcAppPath, fileTemplate))
{
data.Error(TEXT("Failed to load App.cs template."));
return true;
}
- fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstAppPath);
@@ -195,7 +193,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
- (char*)fileTemplate.Get()
+ fileTemplate.Get()
, defaultNamespace.ToStringAnsi() // {0} Default Namespace
);
hasError = file->HasError();
@@ -207,16 +205,15 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
return true;
}
}
- const auto dstFlaxGeneratedPath = data.OutputPath / TEXT("FlaxGenerated.cs");
+ const auto dstFlaxGeneratedPath = data.DataOutputPath / TEXT("FlaxGenerated.cs");
const auto srcFlaxGeneratedPath = uwpDataPath / TEXT("FlaxGenerated.cs");
{
// Get template
- if (File::ReadAllBytes(srcFlaxGeneratedPath, fileTemplate))
+ if (File::ReadAllText(srcFlaxGeneratedPath, fileTemplate))
{
data.Error(TEXT("Failed to load FlaxGenerated.cs template."));
return true;
}
- fileTemplate[fileTemplate.Count() - 1] = 0;
// Prepare
StringAnsi autoRotationPreferences;
@@ -252,7 +249,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
- (char*)fileTemplate.Get()
+ fileTemplate.Get()
, autoRotationPreferences.Get()
, preferredLaunchWindowingMode.Get()
);
@@ -267,17 +264,16 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
// Create solution
- const auto dstSolutionPath = data.OutputPath / projectName + TEXT(".sln");
+ const auto dstSolutionPath = data.DataOutputPath / projectName + TEXT(".sln");
const auto srcSolutionPath = uwpDataPath / TEXT("Solution.sln");
if (!FileSystem::FileExists(dstSolutionPath))
{
// Get template
- if (File::ReadAllBytes(srcSolutionPath, fileTemplate))
+ if (File::ReadAllText(srcSolutionPath, fileTemplate))
{
data.Error(TEXT("Failed to load Solution.sln template."));
return true;
}
- fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstSolutionPath);
@@ -285,7 +281,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
- (char*)fileTemplate.Get()
+ fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Project Name
, mode // {1} Platform Mode
, projectGuid.ToStringAnsi() // {2} Project ID
@@ -301,16 +297,15 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
// Create project
- const auto dstProjectPath = data.OutputPath / projectName + TEXT(".csproj");
+ const auto dstProjectPath = data.DataOutputPath / projectName + TEXT(".csproj");
const auto srcProjectPath = uwpDataPath / TEXT("Project.csproj");
{
// Get template
- if (File::ReadAllBytes(srcProjectPath, fileTemplate))
+ if (File::ReadAllText(srcProjectPath, fileTemplate))
{
data.Error(TEXT("Failed to load Project.csproj template."));
return true;
}
- fileTemplate[fileTemplate.Count() - 1] = 0;
// Build included files data
StringBuilder filesInclude(2048);
@@ -334,7 +329,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
- (char*)fileTemplate.Get()
+ fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Project Name
, mode // {1} Platform Mode
, projectGuid.Get() // {2} Project ID
@@ -352,17 +347,16 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
// Create manifest
- const auto dstManifestPath = data.OutputPath / TEXT("Package.appxmanifest");
+ const auto dstManifestPath = data.DataOutputPath / TEXT("Package.appxmanifest");
const auto srcManifestPath = uwpDataPath / TEXT("Package.appxmanifest");
if (!FileSystem::FileExists(dstManifestPath))
{
// Get template
- if (File::ReadAllBytes(srcManifestPath, fileTemplate))
+ if (File::ReadAllText(srcManifestPath, fileTemplate))
{
data.Error(TEXT("Failed to load Package.appxmanifest template."));
return true;
}
- fileTemplate[fileTemplate.Count() - 1] = 0;
// Build included files data
StringBuilder filesInclude(2048);
@@ -385,7 +379,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
- (char*)fileTemplate.Get()
+ fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Display Name
, gameSettings->CompanyName.ToStringAnsi() // {1} Company Name
, productId.ToStringAnsi() // {2} Product ID
@@ -490,8 +484,8 @@ bool UWPPlatformTools::OnPostProcess(CookingData& data)
// Special case for UWP
// FlaxEngine.dll cannot be added to the solution as `Content` item (due to conflicts with C++ /CX FlaxEngine.dll)
// Use special directory for it (generated UWP project handles this case and copies lib to the output)
- const String assembliesPath = data.OutputPath;
- const auto dstPath1 = data.OutputPath / TEXT("DataSecondary");
+ const String assembliesPath = data.DataOutputPath;
+ const auto dstPath1 = data.DataOutputPath / TEXT("DataSecondary");
if (!FileSystem::DirectoryExists(dstPath1))
{
if (FileSystem::CreateDirectory(dstPath1))
diff --git a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp
index 348930dd5..d5dc1c933 100644
--- a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp
+++ b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp
@@ -36,7 +36,7 @@ ArchitectureType WindowsPlatformTools::GetArchitecture() const
bool WindowsPlatformTools::OnDeployBinaries(CookingData& data)
{
const auto platformSettings = WindowsPlatformSettings::Get();
- const auto& outputPath = data.OutputPath;
+ const auto& outputPath = data.CodeOutputPath;
// Apply executable icon
Array files;
diff --git a/Source/Editor/Cooker/PlatformTools.h b/Source/Editor/Cooker/PlatformTools.h
index c98c008bc..20d578d2e 100644
--- a/Source/Editor/Cooker/PlatformTools.h
+++ b/Source/Editor/Cooker/PlatformTools.h
@@ -144,8 +144,8 @@ public:
AotConfig(CookingData& data)
{
Platform::GetEnvironmentVariables(EnvVars);
- EnvVars[TEXT("MONO_PATH")] = data.OutputPath / TEXT("Mono/lib/mono/4.5");
- AssembliesSearchDirs.Add(data.OutputPath / TEXT("Mono/lib/mono/4.5"));
+ EnvVars[TEXT("MONO_PATH")] = data.DataOutputPath / TEXT("Mono/lib/mono/4.5");
+ AssembliesSearchDirs.Add(data.DataOutputPath / TEXT("Mono/lib/mono/4.5"));
}
};
diff --git a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp
index c5c585f60..5d3125960 100644
--- a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp
+++ b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp
@@ -119,7 +119,7 @@ bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, c
}
for (auto& file : files)
{
- const String dst = data.OutputPath / StringUtils::GetFileName(file);
+ const String dst = data.CodeOutputPath / StringUtils::GetFileName(file);
if (dst != file && FileSystem::CopyFile(dst, file))
{
data.Error(TEXT("Failed to copy file from {0} to {1}."), file, dst);
@@ -180,7 +180,12 @@ bool CompileScriptsStep::Perform(CookingData& data)
platform = TEXT("Android");
architecture = TEXT("ARM64");
break;
+ case BuildPlatform::Switch:
+ platform = TEXT("Switch");
+ architecture = TEXT("ARM64");
+ break;
default:
+ LOG(Error, "Unknown or unsupported build platform.");
return true;
}
_extensionsToSkip.Clear();
@@ -289,7 +294,7 @@ bool CompileScriptsStep::Perform(CookingData& data)
}
writer.EndObject();
- const String outputBuildInfo = data.OutputPath / TEXT("Game.Build.json");
+ const String outputBuildInfo = data.CodeOutputPath / TEXT("Game.Build.json");
if (File::WriteAllBytes(outputBuildInfo, (byte*)buffer.GetString(), (int32)buffer.GetSize()))
{
LOG(Error, "Failed to save binary modules info file {0}.", outputBuildInfo);
diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp
index 84f2ec8f3..5f097f638 100644
--- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp
+++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp
@@ -29,6 +29,7 @@
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Engine/Base/GameBase.h"
+#include "Engine/Engine/Globals.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
#if PLATFORM_TOOLS_WINDOWS
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
@@ -453,6 +454,14 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE);
break;
}
+#endif
+#if PLATFORM_TOOLS_SWITCH
+ case BuildPlatform::Switch:
+ {
+ const char* platformDefineName = "PLATFORM_SWITCH";
+ COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE);
+ break;
+ }
#endif
default:
{
@@ -861,7 +870,7 @@ public:
// Create package
// Note: FlaxStorage::Create overrides chunks locations in file so don't use files anymore (only readonly)
const String localPath = String::Format(TEXT("Content/Data_{0}.{1}"), _packageIndex, PACKAGE_FILES_EXTENSION);
- const String path = data.OutputPath / localPath;
+ const String path = data.DataOutputPath / localPath;
if (FlaxStorage::Create(path, assetsData, false, &CustomData))
{
data.Error(TEXT("Failed to create assets package."));
@@ -1027,7 +1036,7 @@ bool CookAssetsStep::Perform(CookingData& data)
gameFlags |= GameHeaderFlags::ShowSplashScreen;
// Open file
- auto stream = FileWriteStream::Open(data.OutputPath / TEXT("Content/head"));
+ auto stream = FileWriteStream::Open(data.DataOutputPath / TEXT("Content/head"));
if (stream == nullptr)
{
data.Error(TEXT("Failed to create game data file."));
@@ -1121,7 +1130,7 @@ bool CookAssetsStep::Perform(CookingData& data)
BUILD_STEP_CANCEL_CHECK;
// Save assets cache
- if (AssetsCache::Save(data.OutputPath / TEXT("Content/AssetsCache.dat"), AssetsRegistry, AssetPathsMapping, AssetsCacheFlags::RelativePaths))
+ if (AssetsCache::Save(data.DataOutputPath / TEXT("Content/AssetsCache.dat"), AssetsRegistry, AssetPathsMapping, AssetsCacheFlags::RelativePaths))
{
data.Error(TEXT("Failed to create assets registry."));
return true;
diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp
index f90658cab..17ddbf73c 100644
--- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp
+++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp
@@ -7,6 +7,7 @@
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Renderer/ReflectionsPass.h"
#include "Engine/Renderer/AntiAliasing/SMAA.h"
+#include "Engine/Engine/Globals.h"
bool DeployDataStep::Perform(CookingData& data)
{
@@ -15,7 +16,7 @@ bool DeployDataStep::Perform(CookingData& data)
const auto gameSettings = GameSettings::Get();
// Setup output folders and copy required data
- const auto contentDir = data.OutputPath / TEXT("Content");
+ const auto contentDir = data.DataOutputPath / TEXT("Content");
if (FileSystem::DirectoryExists(contentDir))
{
// Remove old content files
@@ -26,7 +27,7 @@ bool DeployDataStep::Perform(CookingData& data)
}
FileSystem::CreateDirectory(contentDir);
const auto srcMono = depsRoot / TEXT("Mono");
- const auto dstMono = data.OutputPath / TEXT("Mono");
+ const auto dstMono = data.DataOutputPath / TEXT("Mono");
if (!FileSystem::DirectoryExists(dstMono))
{
if (!FileSystem::DirectoryExists(srcMono))
diff --git a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp
index 4c482c529..6e5fe50a7 100644
--- a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp
+++ b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp
@@ -24,7 +24,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
data.Tools->OnConfigureAOT(data, config);
// Prepare output directory
- config.AotCachePath = data.OutputPath / TEXT("Mono/lib/mono/aot-cache");
+ config.AotCachePath = data.DataOutputPath / TEXT("Mono/lib/mono/aot-cache");
switch (data.Tools->GetArchitecture())
{
case ArchitectureType::x86:
@@ -52,9 +52,9 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
FileSystem::DirectoryGetFiles(config.Assemblies, dir, TEXT("*.dll"), DirectorySearchOption::TopDirectoryOnly);
for (auto& binaryModule : data.BinaryModules)
if (binaryModule.ManagedPath.HasChars())
- config.Assemblies.Add(data.OutputPath / binaryModule.ManagedPath);
+ config.Assemblies.Add(data.CodeOutputPath / binaryModule.ManagedPath);
// TODO: move AOT to Flax.Build and perform it on all C# assemblies used in target build
- config.Assemblies.Add(data.OutputPath / TEXT("Newtonsoft.Json.dll"));
+ config.Assemblies.Add(data.CodeOutputPath / TEXT("Newtonsoft.Json.dll"));
// Perform AOT for the assemblies
for (int32 i = 0; i < config.Assemblies.Count(); i++)
diff --git a/Source/Editor/Cooker/Steps/ValidateStep.cpp b/Source/Editor/Cooker/Steps/ValidateStep.cpp
index 915f7839d..a64317496 100644
--- a/Source/Editor/Cooker/Steps/ValidateStep.cpp
+++ b/Source/Editor/Cooker/Steps/ValidateStep.cpp
@@ -11,9 +11,17 @@ bool ValidateStep::Perform(CookingData& data)
data.StepProgress(TEXT("Performing validation"), 0);
// Ensure output and cache directories exist
- if (!FileSystem::DirectoryExists(data.OutputPath))
+ if (!FileSystem::DirectoryExists(data.CodeOutputPath))
{
- if (FileSystem::CreateDirectory(data.OutputPath))
+ if (FileSystem::CreateDirectory(data.CodeOutputPath))
+ {
+ data.Error(TEXT("Failed to create build output directory."));
+ return true;
+ }
+ }
+ if (!FileSystem::DirectoryExists(data.DataOutputPath))
+ {
+ if (FileSystem::CreateDirectory(data.DataOutputPath))
{
data.Error(TEXT("Failed to create build output directory."));
return true;
diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs
index c5c628755..ca45652b9 100644
--- a/Source/Editor/CustomEditors/CustomEditor.cs
+++ b/Source/Editor/CustomEditors/CustomEditor.cs
@@ -250,6 +250,15 @@ namespace FlaxEditor.CustomEditors
_children[i].RefreshInternal();
}
+ ///
+ /// Synchronizes the value of the container. Called during Refresh to flush property after editing it in UI.
+ ///
+ /// The value to set.
+ protected virtual void SynchronizeValue(object value)
+ {
+ _values.Set(_parent.Values, value);
+ }
+
internal virtual void RefreshInternal()
{
if (_values == null)
@@ -264,7 +273,7 @@ namespace FlaxEditor.CustomEditors
_valueToSet = null;
// Assign value
- _values.Set(_parent.Values, val);
+ SynchronizeValue(val);
// Propagate values up (eg. when member of structure gets modified, also structure should be updated as a part of the other object)
var obj = _parent;
diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
index 7f19de195..be7b4de5c 100644
--- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs
+++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
@@ -139,7 +139,7 @@ namespace FlaxEditor.CustomEditors
///
protected override void OnModified()
{
- Presenter.Modified?.Invoke();
+ Presenter.OnModified();
base.OnModified();
}
@@ -354,6 +354,14 @@ namespace FlaxEditor.CustomEditors
ExpandGroups(this, false);
}
+ ///
+ /// Invokes event.
+ ///
+ public void OnModified()
+ {
+ Modified?.Invoke();
+ }
+
///
/// Called when selection gets changed.
///
diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cpp b/Source/Editor/CustomEditors/CustomEditorsUtil.cpp
index ffd42d750..519acdf60 100644
--- a/Source/Editor/CustomEditors/CustomEditorsUtil.cpp
+++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cpp
@@ -3,6 +3,7 @@
#include "CustomEditorsUtil.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/DateTime.h"
+#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Scripting/Scripting.h"
diff --git a/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs b/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs
new file mode 100644
index 000000000..521b844ec
--- /dev/null
+++ b/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs
@@ -0,0 +1,429 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using FlaxEditor.Content.Settings;
+using FlaxEditor.CustomEditors.Editors;
+using FlaxEditor.Scripting;
+using FlaxEngine;
+using FlaxEngine.GUI;
+using FlaxEngine.Utilities;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Object = FlaxEngine.Object;
+
+namespace FlaxEditor.CustomEditors.Dedicated
+{
+ [CustomEditor(typeof(LocalizationSettings))]
+ sealed class LocalizationSettingsEditor : GenericEditor
+ {
+ private CultureInfo _theMostTranslatedCulture;
+ private int _theMostTranslatedCultureCount;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ Profiler.BeginEvent("LocalizationSettingsEditor.Initialize");
+ var settings = (LocalizationSettings)Values[0];
+ var tablesLength = settings.LocalizedStringTables?.Length ?? 0;
+ var tables = new List(tablesLength);
+ for (int i = 0; i < tablesLength; i++)
+ {
+ var table = settings.LocalizedStringTables[i];
+ if (table && !table.WaitForLoaded())
+ tables.Add(table);
+ }
+ var locales = tables.GroupBy(x => x.Locale);
+ var tableEntries = new Dictionary>();
+ var allKeys = new HashSet();
+ foreach (var e in locales)
+ {
+ foreach (var table in e)
+ {
+ var entries = table.Entries;
+ tableEntries[table] = entries;
+ allKeys.AddRange(entries.Keys);
+ }
+ }
+
+ {
+ var group = layout.Group("Preview");
+
+ // Current language and culture preview management
+ group.Object("Current Language", new CustomValueContainer(new ScriptType(typeof(CultureInfo)), Localization.CurrentLanguage, (instance, index) => Localization.CurrentLanguage, (instance, index, value) => Localization.CurrentLanguage = value as CultureInfo), null, "Current UI display language for the game preview.");
+ group.Object("Current Culture", new CustomValueContainer(new ScriptType(typeof(CultureInfo)), Localization.CurrentCulture, (instance, index) => Localization.CurrentCulture, (instance, index, value) => Localization.CurrentCulture = value as CultureInfo), null, "Current values formatting culture for the game preview.");
+ }
+
+ {
+ var group = layout.Group("Locales");
+
+ // Show all existing locales
+ _theMostTranslatedCulture = null;
+ _theMostTranslatedCultureCount = -1;
+ foreach (var e in locales)
+ {
+ var culture = new CultureInfo(e.Key);
+ var prop = group.AddPropertyItem(CultureInfoEditor.GetName(culture), culture.NativeName);
+ int count = e.Sum(x => tableEntries[x].Count);
+ int validCount = e.Sum(x => tableEntries[x].Values.Count(y => y != null && y.Length != 0 && !string.IsNullOrEmpty(y[0])));
+ if (count > _theMostTranslatedCultureCount)
+ {
+ _theMostTranslatedCulture = culture;
+ _theMostTranslatedCultureCount = count;
+ }
+ prop.Label(string.Format("Progress: {0}% ({1}/{2})", (int)(((float)validCount / allKeys.Count * 100.0f)), validCount, allKeys.Count));
+ prop.Label("Tables:");
+ foreach (var table in e)
+ {
+ var namePath = table.Path;
+ if (namePath.StartsWith(Globals.ProjectFolder))
+ namePath = namePath.Substring(Globals.ProjectFolder.Length + 1);
+ var tableLabel = prop.ClickableLabel(namePath).CustomControl;
+ tableLabel.TextColorHighlighted = Color.Wheat;
+ tableLabel.DoubleClick += delegate { Editor.Instance.Windows.ContentWin.Select(table); };
+ }
+ group.Space(10);
+ }
+
+ // Update add button
+ var update = group.Button("Update").Button;
+ update.TooltipText = "Refreshes the dashboard statistics";
+ update.Height = 16.0f;
+ update.Clicked += RebuildLayout;
+
+ // New locale add button
+ var addLocale = group.Button("Add Locale...").Button;
+ addLocale.TooltipText = "Shows a locale picker and creates new localization for it with not translated string tables";
+ addLocale.Height = 16.0f;
+ addLocale.ButtonClicked += delegate(Button button)
+ {
+ var menu = CultureInfoEditor.CreatePicker(null, culture =>
+ {
+ var displayName = CultureInfoEditor.GetName(culture);
+ if (locales.Any(x => x.Key == culture.Name))
+ {
+ MessageBox.Show($"Culture '{displayName}' is already added.");
+ return;
+ }
+ Profiler.BeginEvent("LocalizationSettingsEditor.AddLocale");
+ Editor.Log($"Adding culture '{displayName}' to localization settings");
+ var newTables = settings.LocalizedStringTables.ToList();
+ if (_theMostTranslatedCulture != null)
+ {
+ // Duplicate localization for culture with the highest amount of keys
+ var g = locales.First(x => x.Key == _theMostTranslatedCulture.Name);
+ foreach (var e in g)
+ {
+ var path = e.Path;
+ var filename = Path.GetFileNameWithoutExtension(path);
+ if (filename.EndsWith(_theMostTranslatedCulture.Name))
+ filename = filename.Substring(0, filename.Length - _theMostTranslatedCulture.Name.Length);
+ path = Path.Combine(Path.GetDirectoryName(path), filename + culture.Name + ".json");
+ var table = FlaxEngine.Content.CreateVirtualAsset();
+ table.Locale = culture.Name;
+ var entries = new Dictionary();
+ foreach (var ee in tableEntries[e])
+ {
+ var vv = (string[])ee.Value.Clone();
+ for (var i = 0; i < vv.Length; i++)
+ vv[i] = string.Empty;
+ entries.Add(ee.Key, vv);
+ }
+ table.Entries = entries;
+ if (!table.Save(path))
+ {
+ Object.Destroy(table);
+ newTables.Add(FlaxEngine.Content.LoadAsync(path));
+ }
+ }
+ }
+ else
+ {
+ // No localization so initialize with empty table
+ var path = Path.Combine(Path.Combine(Path.GetDirectoryName(GameSettings.Load().Localization.Path), "Localization", culture.Name + ".json"));
+ var table = FlaxEngine.Content.CreateVirtualAsset();
+ table.Locale = culture.Name;
+ if (!table.Save(path))
+ {
+ Object.Destroy(table);
+ newTables.Add(FlaxEngine.Content.LoadAsync(path));
+ }
+ }
+ settings.LocalizedStringTables = newTables.ToArray();
+ Presenter.OnModified();
+ RebuildLayout();
+ Profiler.EndEvent();
+ });
+ menu.Show(button, new Vector2(0, button.Height));
+ };
+
+ // Export button
+ var exportLocalization = group.Button("Export...").Button;
+ exportLocalization.TooltipText = "Exports the localization strings into .pot file for translation";
+ exportLocalization.Height = 16.0f;
+ exportLocalization.Clicked += delegate
+ {
+ if (FileSystem.ShowSaveFileDialog(null, null, "*.pot", false, "Export localization for translation to .pot file", out var filenames))
+ return;
+ Profiler.BeginEvent("LocalizationSettingsEditor.Export");
+ if (!filenames[0].EndsWith(".pot"))
+ filenames[0] += ".pot";
+ var nplurals = 1;
+ foreach (var e in tableEntries)
+ {
+ foreach (var value in e.Value.Values)
+ {
+ if (value != null && value.Length > nplurals)
+ nplurals = value.Length;
+ }
+ }
+ using (var writer = new StreamWriter(filenames[0], false, Encoding.UTF8))
+ {
+ writer.WriteLine("msgid \"\"");
+ writer.WriteLine("msgstr \"\"");
+ writer.WriteLine("\"Language: English\\n\"");
+ writer.WriteLine("\"MIME-Version: 1.0\\n\"");
+ writer.WriteLine("\"Content-Type: text/plain; charset=UTF-8\\n\"");
+ writer.WriteLine("\"Content-Transfer-Encoding: 8bit\\n\"");
+ writer.WriteLine($"\"Plural-Forms: nplurals={nplurals}; plural=(n != 1);\\n\"");
+ writer.WriteLine("\"X-Generator: FlaxEngine\\n\"");
+ var written = new HashSet();
+ foreach (var e in tableEntries)
+ {
+ foreach (var pair in e.Value)
+ {
+ if (written.Contains(pair.Key))
+ continue;
+ written.Add(pair.Key);
+
+ writer.WriteLine("");
+ writer.WriteLine($"msgid \"{pair.Key}\"");
+ if (pair.Value == null || pair.Value.Length < 2)
+ {
+ writer.WriteLine("msgstr \"\"");
+ }
+ else
+ {
+ writer.WriteLine("msgid_plural \"\"");
+ for (int i = 0; i < pair.Value.Length; i++)
+ writer.WriteLine($"msgstr[{i}] \"\"");
+ }
+ }
+ if (written.Count == allKeys.Count)
+ break;
+ }
+ }
+ Profiler.EndEvent();
+ };
+
+ // Find localized strings in code button
+ var findStringsCode = group.Button("Find localized strings in code").Button;
+ findStringsCode.TooltipText = "Searches for localized string usage in inside a project source files";
+ findStringsCode.Height = 16.0f;
+ findStringsCode.Clicked += delegate
+ {
+ var newKeys = new Dictionary();
+ Profiler.BeginEvent("LocalizationSettingsEditor.FindLocalizedStringsInSource");
+
+ // C#
+ var files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cs", SearchOption.AllDirectories);
+ var filesCount = files.Length;
+ foreach (var file in files)
+ FindNewKeysCSharp(file, newKeys, allKeys);
+
+ // C++
+ files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories);
+ filesCount += files.Length;
+ foreach (var file in files)
+ FindNewKeysCpp(file, newKeys, allKeys);
+ files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories);
+ filesCount += files.Length;
+ foreach (var file in files)
+ FindNewKeysCpp(file, newKeys, allKeys);
+
+ AddNewKeys(newKeys, filesCount, locales, tableEntries);
+ Profiler.EndEvent();
+ };
+
+ // Find localized strings in content button
+ var findStringsContent = group.Button("Find localized strings in content").Button;
+ findStringsContent.TooltipText = "Searches for localized string usage in inside a project content files (scenes, prefabs)";
+ findStringsContent.Height = 16.0f;
+ findStringsContent.Clicked += delegate
+ {
+ var newKeys = new Dictionary();
+ Profiler.BeginEvent("LocalizationSettingsEditor.FindLocalizedStringsInContent");
+
+ // Scenes
+ var files = Directory.GetFiles(Globals.ProjectContentFolder, "*.scene", SearchOption.AllDirectories);
+ var filesCount = files.Length;
+ foreach (var file in files)
+ FindNewKeysJson(file, newKeys, allKeys);
+
+ // Prefabs
+ files = Directory.GetFiles(Globals.ProjectContentFolder, "*.prefab", SearchOption.AllDirectories);
+ filesCount += files.Length;
+ foreach (var file in files)
+ FindNewKeysJson(file, newKeys, allKeys);
+
+ AddNewKeys(newKeys, filesCount, locales, tableEntries);
+ Profiler.EndEvent();
+ };
+ }
+
+ {
+ // Raw asset data editing
+ var group = layout.Group("Data");
+ base.Initialize(group);
+ }
+
+ Profiler.EndEvent();
+ }
+
+ private static void FindNewKeysCSharp(string file, Dictionary newKeys, HashSet allKeys)
+ {
+ var startToken = "Localization.GetString";
+ var textToken = "\"";
+ FindNewKeys(file, newKeys, allKeys, startToken, textToken);
+ }
+
+ private static void FindNewKeysCpp(string file, Dictionary newKeys, HashSet allKeys)
+ {
+ var startToken = "Localization::GetString";
+ var textToken = "TEXT(\"";
+ FindNewKeys(file, newKeys, allKeys, startToken, textToken);
+ }
+
+ private static void FindNewKeys(string file, Dictionary newKeys, HashSet allKeys, string startToken, string textToken)
+ {
+ var contents = File.ReadAllText(file);
+ var idx = contents.IndexOf(startToken);
+ while (idx != -1)
+ {
+ idx += startToken.Length + 1;
+ int braces = 1;
+ int start = idx;
+ while (idx < contents.Length && braces != 0)
+ {
+ if (contents[idx] == '(')
+ braces++;
+ if (contents[idx] == ')')
+ braces--;
+ idx++;
+ }
+ if (idx == contents.Length)
+ break;
+ var inside = contents.Substring(start, idx - start - 1);
+ var textStart = inside.IndexOf(textToken);
+ if (textStart != -1)
+ {
+ textStart += textToken.Length;
+ var textEnd = textStart;
+ while (textEnd < inside.Length && inside[textEnd] != '\"')
+ {
+ if (inside[textEnd] == '\\')
+ textEnd++;
+ textEnd++;
+ }
+ var id = inside.Substring(textStart, textEnd - textStart);
+ textStart = inside.Length > textEnd + 2 ? inside.IndexOf(textToken, textEnd + 2) : -1;
+ string value = null;
+ if (textStart != -1)
+ {
+ textStart += textToken.Length;
+ textEnd = textStart;
+ while (textEnd < inside.Length && inside[textEnd] != '\"')
+ {
+ if (inside[textEnd] == '\\')
+ textEnd++;
+ textEnd++;
+ }
+ value = inside.Substring(textStart, textEnd - textStart);
+ }
+
+ if (!allKeys.Contains(id))
+ newKeys[id] = value;
+ }
+
+ idx = contents.IndexOf(startToken, idx);
+ }
+ }
+
+ private static void FindNewKeysJson(Dictionary newKeys, HashSet allKeys, JToken token)
+ {
+ if (token is JObject o)
+ {
+ foreach (var p in o)
+ {
+ if (string.Equals(p.Key, "Id", StringComparison.Ordinal) && p.Value is JValue i && i.Value is string id && !allKeys.Contains(id))
+ {
+ var count = o.Properties().Count();
+ if (count == 1)
+ {
+ newKeys[id] = null;
+ return;
+ }
+ if (count == 2)
+ {
+ var v = o.Property("Value")?.Value as JValue;
+ if (v?.Value is string value)
+ {
+ newKeys[id] = value;
+ return;
+ }
+ }
+ }
+ FindNewKeysJson(newKeys, allKeys, p.Value);
+ }
+ }
+ else if (token is JArray a)
+ {
+ foreach (var p in a)
+ {
+ FindNewKeysJson(newKeys, allKeys, p);
+ }
+ }
+ }
+
+ private static void FindNewKeysJson(string file, Dictionary newKeys, HashSet allKeys)
+ {
+ using (var reader = new StreamReader(file))
+ using (var jsonReader = new JsonTextReader(reader))
+ {
+ var token = JToken.ReadFrom(jsonReader);
+ FindNewKeysJson(newKeys, allKeys, token);
+ }
+ }
+
+ private void AddNewKeys(Dictionary newKeys, int filesCount, IEnumerable> locales, Dictionary> tableEntries)
+ {
+ Editor.Log($"Found {newKeys.Count} new localized strings in {filesCount} files");
+ if (newKeys.Count == 0)
+ return;
+ foreach (var e in newKeys)
+ Editor.Log(e.Key + (e.Value != null ? " = " + e.Value : string.Empty));
+ foreach (var locale in locales)
+ {
+ var table = locale.First();
+ var entries = tableEntries[table];
+ if (table.Locale == "en")
+ {
+ foreach (var e in newKeys)
+ entries[e.Key] = new[] { e.Value };
+ }
+ else
+ {
+ foreach (var e in newKeys)
+ entries[e.Key] = new[] { string.Empty };
+ }
+ table.Entries = entries;
+ table.Save();
+ }
+ RebuildLayout();
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
index e176f2b70..dcd70a340 100644
--- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs
@@ -32,17 +32,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (_presets != value)
{
_presets = value;
- OnPresetsChanged();
+ TooltipText = CustomEditorsUtil.GetPropertyNameUI(_presets.ToString());
}
}
}
public bool IsSelected;
-
- private void OnPresetsChanged()
- {
- TooltipText = CustomEditorsUtil.GetPropertyNameUI(_presets.ToString());
- }
+ public bool SupportsShiftModulation;
///
public override void Draw()
@@ -79,6 +75,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
borderColor = BorderColorHighlighted;
}
+ if (SupportsShiftModulation && Input.GetKey(KeyboardKeys.Shift))
+ {
+ backgroundColor = BackgroundColorSelected;
+ }
+
// Calculate fill area
float fillSize = rect.Width / 3;
Rectangle fillArea;
@@ -154,24 +155,83 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
Render2D.DrawRectangle(rect, style.BackgroundSelected.AlphaMultiplied(0.8f), 1.1f);
}
+
+ // Draw pivot point
+ if (SupportsShiftModulation && Input.GetKey(KeyboardKeys.Control))
+ {
+ Vector2 pivotPoint;
+ switch (_presets)
+ {
+ case AnchorPresets.Custom:
+ pivotPoint = Vector2.Minimum;
+ break;
+ case AnchorPresets.TopLeft:
+ pivotPoint = new Vector2(0, 0);
+ break;
+ case AnchorPresets.TopCenter:
+ case AnchorPresets.HorizontalStretchTop:
+ pivotPoint = new Vector2(rect.Width / 2, 0);
+ break;
+ case AnchorPresets.TopRight:
+ pivotPoint = new Vector2(rect.Width, 0);
+ break;
+ case AnchorPresets.MiddleLeft:
+ case AnchorPresets.VerticalStretchLeft:
+ pivotPoint = new Vector2(0, rect.Height / 2);
+ break;
+ case AnchorPresets.MiddleCenter:
+ case AnchorPresets.VerticalStretchCenter:
+ case AnchorPresets.HorizontalStretchMiddle:
+ case AnchorPresets.StretchAll:
+ pivotPoint = new Vector2(rect.Width / 2, rect.Height / 2);
+ break;
+ case AnchorPresets.MiddleRight:
+ case AnchorPresets.VerticalStretchRight:
+ pivotPoint = new Vector2(rect.Width, rect.Height / 2);
+ break;
+ case AnchorPresets.BottomLeft:
+ pivotPoint = new Vector2(0, rect.Height);
+ break;
+ case AnchorPresets.BottomCenter:
+ case AnchorPresets.HorizontalStretchBottom:
+ pivotPoint = new Vector2(rect.Width / 2, rect.Height);
+ break;
+ case AnchorPresets.BottomRight:
+ pivotPoint = new Vector2(rect.Width, rect.Height);
+ break;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ var pivotPointSize = new Vector2(3.0f);
+ Render2D.DrawRectangle(new Rectangle(pivotPoint - pivotPointSize * 0.5f, pivotPointSize), style.ProgressNormal, 1.1f);
+ }
}
}
- class AnchorPresetsEditorPopup : ContextMenuBase
+ ///
+ /// Context menu for anchors presets editing.
+ ///
+ ///
+ public sealed class AnchorPresetsEditorPopup : ContextMenuBase
{
const float ButtonsMargin = 10.0f;
const float ButtonsMarginStretch = 8.0f;
const float ButtonsSize = 32.0f;
const float TitleHeight = 23.0f;
+ const float InfoHeight = 23.0f;
const float DialogWidth = ButtonsSize * 4 + ButtonsMargin * 5 + ButtonsMarginStretch;
- const float DialogHeight = TitleHeight + ButtonsSize * 4 + ButtonsMargin * 5 + ButtonsMarginStretch;
+ const float DialogHeight = TitleHeight + InfoHeight + ButtonsSize * 4 + ButtonsMargin * 5 + ButtonsMarginStretch;
+
+ private readonly bool _supportsShiftModulation;
///
/// Initializes a new instance of the class.
///
/// The initial value.
- public AnchorPresetsEditorPopup(AnchorPresets presets)
+ /// If the popup should react to shift
+ public AnchorPresetsEditorPopup(AnchorPresets presets, bool supportsShiftModulation = true)
{
+ _supportsShiftModulation = supportsShiftModulation;
+
var style = FlaxEngine.GUI.Style.Current;
Tag = presets;
Size = new Vector2(DialogWidth, DialogHeight);
@@ -184,9 +244,17 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = this
};
+ // Info
+ var info = new Label(0, title.Bottom, DialogWidth, InfoHeight)
+ {
+ Font = new FontReference(style.FontSmall),
+ Text = "Shift: also set bounds\nControl: also set pivot",
+ Parent = this
+ };
+
// Buttons
var buttonsX = ButtonsMargin;
- var buttonsY = title.Bottom + ButtonsMargin;
+ var buttonsY = info.Bottom + ButtonsMargin;
var buttonsSpacingX = ButtonsSize + ButtonsMargin;
var buttonsSpacingY = ButtonsSize + ButtonsMargin;
//
@@ -219,6 +287,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = this,
Presets = presets,
IsSelected = presets == (AnchorPresets)Tag,
+ SupportsShiftModulation = _supportsShiftModulation,
Tag = presets,
};
button.ButtonClicked += OnButtonClicked;
@@ -278,7 +347,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
private void OnButtonClicked()
{
var location = _button.Center + new Vector2(3.0f);
- var editor = new AnchorPresetsEditorPopup(_button.Presets);
+ var editor = new AnchorPresetsEditorPopup(_button.Presets, true);
editor.VisibleChanged += OnEditorVisibleChanged;
editor.Show(_button.Parent, location);
}
@@ -290,6 +359,30 @@ namespace FlaxEditor.CustomEditors.Dedicated
SetValue(control.Tag);
}
+ ///
+ protected override void SynchronizeValue(object value)
+ {
+ // Custom anchors editing for Control to handle bounds preservation via key modifiers
+ if (ParentEditor != null)
+ {
+ var centerToPosition = Input.GetKey(KeyboardKeys.Shift);
+ var setPivot = Input.GetKey(KeyboardKeys.Control);
+ var editedAny = false;
+ foreach (var parentValue in ParentEditor.Values)
+ {
+ if (parentValue is Control parentControl)
+ {
+ parentControl.SetAnchorPreset((AnchorPresets)value, !centerToPosition, setPivot);
+ editedAny = true;
+ }
+ }
+ if (editedAny)
+ return;
+ }
+
+ base.SynchronizeValue(value);
+ }
+
///
public override void Refresh()
{
@@ -311,7 +404,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// Dedicated custom editor for object.
///
///
- public sealed class UIControlControlEditor : GenericEditor
+ public class UIControlControlEditor : GenericEditor
{
private Type _cachedType;
@@ -423,9 +516,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
private void BuildLocationSizeOffsets(LayoutElementsContainer horUp, LayoutElementsContainer horDown, bool xEq, bool yEq, ScriptType[] valueTypes)
{
- ScriptMemberInfo xInfo = valueTypes[0].GetProperty("X");
+ ScriptMemberInfo xInfo = valueTypes[0].GetProperty("LocalX");
ItemInfo xItem = new ItemInfo(xInfo);
- ScriptMemberInfo yInfo = valueTypes[0].GetProperty("Y");
+ ScriptMemberInfo yInfo = valueTypes[0].GetProperty("LocalY");
ItemInfo yItem = new ItemInfo(yInfo);
ScriptMemberInfo widthInfo = valueTypes[0].GetProperty("Width");
ItemInfo widthItem = new ItemInfo(widthInfo);
diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
index 2e5aeade7..141330aea 100644
--- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
@@ -154,9 +154,20 @@ namespace FlaxEditor.CustomEditors.Editors
if (i != 0 && spacing > 0f)
{
if (layout.Children.Count > 0 && layout.Children[layout.Children.Count - 1] is PropertiesListElement propertiesListElement)
+ {
+ if (propertiesListElement.Labels.Count > 0)
+ {
+ var label = propertiesListElement.Labels[propertiesListElement.Labels.Count - 1];
+ var margin = label.Margin;
+ margin.Bottom += spacing;
+ label.Margin = margin;
+ }
propertiesListElement.Space(spacing);
+ }
else
+ {
layout.Space(spacing);
+ }
}
var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null;
diff --git a/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs b/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs
new file mode 100644
index 000000000..67533ed56
--- /dev/null
+++ b/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs
@@ -0,0 +1,164 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using FlaxEditor.GUI;
+using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.GUI.Tree;
+using FlaxEditor.Utilities;
+using FlaxEngine;
+using FlaxEngine.GUI;
+
+namespace FlaxEditor.CustomEditors.Editors
+{
+ ///
+ /// Default implementation of the inspector used to edit value type properties. Supports editing property of type (as culture name).
+ ///
+ [CustomEditor(typeof(CultureInfo)), DefaultEditor]
+ internal class CultureInfoEditor : CustomEditor
+ {
+ private ClickableLabel _label;
+
+ ///
+ public override DisplayStyle Style => DisplayStyle.Inline;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ _label = layout.ClickableLabel(GetName(Culture)).CustomControl;
+ _label.RightClick += ShowPicker;
+ var button = new Button
+ {
+ Width = 16.0f,
+ Text = "...",
+ Parent = _label,
+ };
+ button.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
+ button.Clicked += ShowPicker;
+ }
+
+ ///
+ public override void Refresh()
+ {
+ base.Refresh();
+
+ _label.Text = GetName(Culture);
+ }
+
+ ///
+ protected override void Deinitialize()
+ {
+ _label = null;
+
+ base.Deinitialize();
+ }
+
+ private CultureInfo Culture
+ {
+ get
+ {
+ if (Values[0] is CultureInfo asCultureInfo)
+ return asCultureInfo;
+ if (Values[0] is string asString)
+ return new CultureInfo(asString);
+ return null;
+ }
+ set
+ {
+ if (Values[0] is CultureInfo)
+ SetValue(value);
+ else if (Values[0] is string)
+ SetValue(value.Name);
+ }
+ }
+
+ private class CultureInfoComparer : IComparer
+ {
+ public int Compare(CultureInfo a, CultureInfo b)
+ {
+ return string.Compare(a.Name, b.Name, StringComparison.Ordinal);
+ }
+ }
+
+ private static void UpdateFilter(TreeNode node, string filterText)
+ {
+ // Update children
+ bool isAnyChildVisible = false;
+ for (int i = 0; i < node.Children.Count; i++)
+ {
+ if (node.Children[i] is TreeNode child)
+ {
+ UpdateFilter(child, filterText);
+ isAnyChildVisible |= child.Visible;
+ }
+ }
+
+ // Update itself
+ bool noFilter = string.IsNullOrWhiteSpace(filterText);
+ bool isThisVisible = noFilter || QueryFilterHelper.Match(filterText, node.Text);
+ bool isExpanded = isAnyChildVisible;
+ if (isExpanded)
+ node.Expand(true);
+ else
+ node.Collapse(true);
+ node.Visible = isThisVisible | isAnyChildVisible;
+ }
+
+ private void ShowPicker()
+ {
+ var menu = CreatePicker(Culture, value => { Culture = value; });
+ menu.Show(_label, new Vector2(0, _label.Height));
+ }
+
+ internal static ContextMenuBase CreatePicker(CultureInfo value, Action changed)
+ {
+ var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree);
+ tree.Margin = new Margin(-16.0f, 0.0f, -16.0f, -0.0f); // Hide root node
+ var root = tree.AddChild();
+ var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
+ Array.Sort(cultures, 1, cultures.Length - 2, new CultureInfoComparer()); // at 0 there is Invariant Culture
+ var lcidToNode = new Dictionary();
+ for (var i = 0; i < cultures.Length; i++)
+ {
+ var culture = cultures[i];
+ var node = new TreeNode
+ {
+ Tag = culture,
+ Text = GetName(culture),
+ };
+ if (!lcidToNode.TryGetValue(culture.Parent.LCID, out ContainerControl parent))
+ parent = root;
+ node.Parent = parent;
+ lcidToNode[culture.LCID] = node;
+ }
+ if (value != null)
+ tree.Select((TreeNode)lcidToNode[value.LCID]);
+ tree.SelectedChanged += delegate(List before, List after)
+ {
+ if (after.Count == 1)
+ {
+ menu.Hide();
+ changed((CultureInfo)after[0].Tag);
+ }
+ };
+ searchBox.TextChanged += delegate
+ {
+ if (tree.IsLayoutLocked)
+ return;
+ root.LockChildrenRecursive();
+ var query = searchBox.Text;
+ UpdateFilter(root, query);
+ root.UnlockChildrenRecursive();
+ menu.PerformLayout();
+ };
+ root.ExpandAll(true);
+ return menu;
+ }
+
+ internal static string GetName(CultureInfo value)
+ {
+ return value != null ? string.Format("{0} - {1}", value.Name, value.EnglishName) : null;
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
index 4e4048dba..4a94136db 100644
--- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
@@ -61,7 +61,7 @@ namespace FlaxEditor.CustomEditors.Editors
var keyType = _editor.Values.Type.GetGenericArguments()[0];
if (keyType == typeof(string) || keyType.IsPrimitive)
{
- var popup = RenamePopup.Show(Parent, Bounds, Text, false);
+ var popup = RenamePopup.Show(Parent, Rectangle.Margin(Bounds, Margin), Text, false);
popup.Validate += (renamePopup, value) =>
{
object newKey;
@@ -86,7 +86,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
else if (keyType.IsEnum)
{
- var popup = RenamePopup.Show(Parent, Bounds, Text, false);
+ var popup = RenamePopup.Show(Parent, Rectangle.Margin(Bounds, Margin), Text, false);
var picker = new EnumComboBox(keyType)
{
AnchorPreset = AnchorPresets.StretchAll,
@@ -220,9 +220,20 @@ namespace FlaxEditor.CustomEditors.Editors
if (i != 0 && spacing > 0f)
{
if (layout.Children.Count > 0 && layout.Children[layout.Children.Count - 1] is PropertiesListElement propertiesListElement)
+ {
+ if (propertiesListElement.Labels.Count > 0)
+ {
+ var label = propertiesListElement.Labels[propertiesListElement.Labels.Count - 1];
+ var margin = label.Margin;
+ margin.Bottom += spacing;
+ label.Margin = margin;
+ }
propertiesListElement.Space(spacing);
+ }
else
+ {
layout.Space(spacing);
+ }
}
var key = keys.ElementAt(i);
diff --git a/Source/Editor/CustomEditors/Editors/EnumEditor.cs b/Source/Editor/CustomEditors/Editors/EnumEditor.cs
index 7954e18bf..b91c0cf95 100644
--- a/Source/Editor/CustomEditors/Editors/EnumEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/EnumEditor.cs
@@ -9,7 +9,7 @@ using FlaxEngine;
namespace FlaxEditor.CustomEditors.Editors
{
///
- /// Default implementation of the inspector used to edit float value type properties.
+ /// Default implementation of the inspector used to edit enum value type properties.
///
[CustomEditor(typeof(Enum)), DefaultEditor]
public class EnumEditor : CustomEditor
diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
index 264aaeeff..179f5b935 100644
--- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
@@ -214,6 +214,7 @@ namespace FlaxEditor.CustomEditors.Editors
public ScriptMemberInfo Target;
public ScriptMemberInfo Source;
public PropertiesListElement PropertiesList;
+ public GroupElement Group;
public bool Invert;
public int LabelIndex;
@@ -379,26 +380,22 @@ namespace FlaxEditor.CustomEditors.Editors
}
}
}
- if (item.VisibleIf != null)
+ if (item.VisibleIf != null && itemLayout.Children.Count > 0)
{
- PropertiesListElement list;
- if (itemLayout.Children.Count > 0 && itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement list1)
- {
+ PropertiesListElement list = null;
+ GroupElement group = null;
+ if (itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement list1)
list = list1;
- }
+ else if (itemLayout.Children[itemLayout.Children.Count - 1] is GroupElement group1)
+ group = group1;
else
- {
- // TODO: support inlined objects hiding?
return;
- }
// Get source member used to check rule
var sourceMember = GetVisibleIfSource(item.Info.DeclaringType, item.VisibleIf);
if (sourceMember == ScriptType.Null)
return;
- // Find the target control to show/hide
-
// Resize cache
if (_visibleIfCaches == null)
_visibleIfCaches = new VisibleIfCache[8];
@@ -414,6 +411,7 @@ namespace FlaxEditor.CustomEditors.Editors
Target = item.Info,
Source = sourceMember,
PropertiesList = list,
+ Group = group,
LabelIndex = labelIndex,
Invert = item.VisibleIf.Invert,
};
@@ -569,8 +567,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
for (int i = 0; i < _visibleIfCaches.Length; i++)
{
- var c = _visibleIfCaches[i];
-
+ ref var c = ref _visibleIfCaches[i];
if (c.Target == ScriptMemberInfo.Null)
break;
@@ -586,7 +583,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
// Apply the visibility (note: there may be no label)
- if (c.LabelIndex != -1 && c.PropertiesList.Labels.Count > c.LabelIndex)
+ if (c.LabelIndex != -1 && c.PropertiesList != null && c.PropertiesList.Labels.Count > c.LabelIndex)
{
var label = c.PropertiesList.Labels[c.LabelIndex];
label.Visible = visible;
@@ -599,6 +596,10 @@ namespace FlaxEditor.CustomEditors.Editors
child.Visible = visible;
}
}
+ if (c.Group != null)
+ {
+ c.Group.Panel.Visible = visible;
+ }
}
}
catch (Exception ex)
diff --git a/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs b/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs
new file mode 100644
index 000000000..82503d9bc
--- /dev/null
+++ b/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs
@@ -0,0 +1,234 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using FlaxEditor.Content.Settings;
+using FlaxEditor.CustomEditors.Elements;
+using FlaxEditor.GUI.Tree;
+using FlaxEditor.Scripting;
+using FlaxEditor.Utilities;
+using FlaxEngine;
+using FlaxEngine.GUI;
+using Utils = FlaxEditor.Utilities.Utils;
+
+namespace FlaxEditor.CustomEditors.Editors
+{
+ ///
+ /// Default implementation of the inspector used to edit localized string properties.
+ ///
+ [CustomEditor(typeof(LocalizedString)), DefaultEditor]
+ public sealed class LocalizedStringEditor : GenericEditor
+ {
+ private TextBoxElement _idElement, _valueElement;
+
+ ///
+ public override DisplayStyle Style => DisplayStyle.Inline;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ base.Initialize(layout);
+
+ if (layout.Children.Count == 0)
+ return;
+ var propList = layout.Children[layout.Children.Count - 1] as PropertiesListElement;
+ if (propList == null || propList.Children.Count != 2)
+ return;
+ var idElement = propList.Children[0] as TextBoxElement;
+ var valueElement = propList.Children[1] as TextBoxElement;
+ if (idElement == null || valueElement == null)
+ return;
+ _idElement = idElement;
+ _valueElement = valueElement;
+
+ var attributes = Values.GetAttributes();
+ var multiLine = attributes?.FirstOrDefault(x => x is MultilineTextAttribute);
+ if (multiLine != null)
+ {
+ valueElement.TextBox.IsMultiline = true;
+ valueElement.TextBox.Height *= 3;
+ }
+
+ var selectString = new Button
+ {
+ Width = 16.0f,
+ Text = "...",
+ TooltipText = "Select localized text from Localization Settings...",
+ Parent = idElement.TextBox,
+ };
+ selectString.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
+ selectString.ButtonClicked += OnSelectStringClicked;
+
+ var addString = new Button
+ {
+ Width = 16.0f,
+ Text = "+",
+ TooltipText = "Add new localized text to Localization Settings (all used locales)",
+ Parent = _valueElement.TextBox,
+ Enabled = IsSingleObject,
+ };
+ addString.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
+ addString.ButtonClicked += OnAddStringClicked;
+ }
+
+ ///
+ internal override void RefreshInternal()
+ {
+ base.RefreshInternal();
+
+ if (_valueElement != null)
+ {
+ _valueElement.TextBox.WatermarkText = Localization.GetString(_idElement.Text);
+ }
+ }
+
+ ///
+ protected override void Deinitialize()
+ {
+ base.Deinitialize();
+
+ _idElement = null;
+ _valueElement = null;
+ }
+
+ private void OnSelectStringClicked(Button button)
+ {
+ var settings = GameSettings.Load();
+ if (settings?.LocalizedStringTables == null || settings.LocalizedStringTables.Length == 0)
+ {
+ MessageBox.Show("No valid localization settings setup.");
+ return;
+ }
+ Profiler.BeginEvent("LocalizedStringEditor.OnSelectStringClicked");
+ var allKeys = new HashSet();
+ for (int i = 0; i < settings.LocalizedStringTables.Length; i++)
+ {
+ var table = settings.LocalizedStringTables[i];
+ if (table && !table.WaitForLoaded())
+ {
+ var entries = table.Entries;
+ foreach (var e in entries)
+ allKeys.Add(e.Key);
+ }
+ }
+ var allKeysSorted = allKeys.ToList();
+ allKeysSorted.Sort();
+ var value = _idElement?.TextBox.Text;
+ var menu = Utils.CreateSearchPopup(out var searchBox, out var tree);
+ var idToNode = new TreeNode[allKeysSorted.Count];
+ for (var i = 0; i < allKeysSorted.Count; i++)
+ {
+ var key = allKeysSorted[i];
+ var node = new TreeNode
+ {
+ Text = key,
+ TooltipText = Localization.GetString(key),
+ Parent = tree,
+ };
+ if (key == value)
+ tree.Select(node);
+ idToNode[i] = node;
+ }
+ tree.SelectedChanged += delegate(List before, List after)
+ {
+ if (after.Count == 1)
+ {
+ menu.Hide();
+ _idElement.TextBox.SetTextAsUser(after[0].Text);
+ }
+ };
+ searchBox.TextChanged += delegate
+ {
+ if (tree.IsLayoutLocked)
+ return;
+ tree.LockChildrenRecursive();
+ var query = searchBox.Text;
+ for (int i = 0; i < idToNode.Length; i++)
+ {
+ var node = idToNode[i];
+ node.Visible = string.IsNullOrWhiteSpace(query) || QueryFilterHelper.Match(query, node.Text);
+ }
+ tree.UnlockChildrenRecursive();
+ menu.PerformLayout();
+ };
+ menu.Show(button, new Vector2(0, button.Height));
+ Profiler.EndEvent();
+ }
+
+ private void OnAddStringClicked(Button button)
+ {
+ var settings = GameSettings.Load();
+ if (settings?.LocalizedStringTables == null || settings.LocalizedStringTables.Length == 0)
+ {
+ MessageBox.Show("No valid localization settings setup.");
+ return;
+ }
+ Profiler.BeginEvent("LocalizedStringEditor.OnAddStringClicked");
+ var allKeys = new HashSet();
+ for (int i = 0; i < settings.LocalizedStringTables.Length; i++)
+ {
+ var table = settings.LocalizedStringTables[i];
+ if (table && !table.WaitForLoaded())
+ {
+ var entries = table.Entries;
+ foreach (var e in entries)
+ allKeys.Add(e.Key);
+ }
+ }
+ _valueElement.TextBox.SetTextAsUser(null);
+ string newKey = null;
+ if (string.IsNullOrEmpty(_idElement.Text))
+ {
+ CustomEditor customEditor = this;
+ while (customEditor?.Values != null)
+ {
+ if (customEditor.Values.Info != ScriptMemberInfo.Null)
+ if (newKey == null)
+ newKey = customEditor.Values.Info.Name;
+ else
+ newKey = customEditor.Values.Info.Name + '.' + newKey;
+ else if (customEditor.Values[0] is SceneObject sceneObject)
+ if (newKey == null)
+ newKey = sceneObject.GetNamePath('.');
+ else
+ newKey = sceneObject.GetNamePath('.') + '.' + newKey;
+ else
+ break;
+ customEditor = customEditor.ParentEditor;
+ }
+ if (string.IsNullOrWhiteSpace(newKey))
+ newKey = Guid.NewGuid().ToString("N");
+ }
+ else
+ {
+ newKey = _idElement.Text;
+ }
+ if (allKeys.Contains(newKey))
+ {
+ Profiler.EndEvent();
+ if (_idElement.Text != newKey)
+ _idElement.TextBox.SetTextAsUser(newKey);
+ else
+ MessageBox.Show("Already added.");
+ return;
+ }
+ var newValue = _valueElement.Text;
+ Editor.Log(newKey + (newValue != null ? " = " + newValue : string.Empty));
+ var locales = settings.LocalizedStringTables.GroupBy(x => x.Locale);
+ foreach (var locale in locales)
+ {
+ var table = locale.First();
+ var entries = table.Entries;
+ if (table.Locale == "en")
+ entries[newKey] = new[] { newValue };
+ else
+ entries[newKey] = new[] { string.Empty };
+ table.Entries = entries;
+ table.Save();
+ }
+ _idElement.TextBox.SetTextAsUser(newKey);
+ Profiler.EndEvent();
+ }
+ }
+}
diff --git a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
index 7724acf18..dcdf3114b 100644
--- a/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
@@ -3,7 +3,6 @@
using System;
using FlaxEditor.GUI;
using FlaxEditor.Scripting;
-using FlaxEngine;
namespace FlaxEditor.CustomEditors.Editors
{
diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
index 338c8eca6..f80c81e27 100644
--- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
@@ -12,6 +12,9 @@ namespace FlaxEditor.CustomEditors.Editors
[CustomEditor(typeof(Quaternion)), DefaultEditor]
public class QuaternionEditor : CustomEditor
{
+ private Vector3 _cachedAngles = Vector3.Zero;
+ private object _cachedToken;
+
///
/// The X component element
///
@@ -58,15 +61,36 @@ namespace FlaxEditor.CustomEditors.Editors
if (IsSetBlocked)
return;
- float x = XElement.FloatValue.Value;
- float y = YElement.FloatValue.Value;
- float z = ZElement.FloatValue.Value;
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
var token = isSliding ? this : null;
+ var useCachedAngles = isSliding && token == _cachedToken;
+
+ float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.FloatValue.Value;
+ float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.FloatValue.Value;
+ float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.FloatValue.Value;
+
+ x = Mathf.UnwindDegrees(x);
+ y = Mathf.UnwindDegrees(y);
+ z = Mathf.UnwindDegrees(z);
+
+ if (!useCachedAngles)
+ {
+ _cachedAngles = new Vector3(x, y, z);
+ }
+
+ _cachedToken = token;
+
Quaternion.Euler(x, y, z, out Quaternion value);
SetValue(value, token);
}
+ ///
+ protected override void ClearToken()
+ {
+ _cachedToken = null;
+ base.ClearToken();
+ }
+
///
public override void Refresh()
{
diff --git a/Source/Editor/CustomEditors/Elements/Container/HorizontalPanelElement.cs b/Source/Editor/CustomEditors/Elements/Container/HorizontalPanelElement.cs
index 2958cd337..de015a371 100644
--- a/Source/Editor/CustomEditors/Elements/Container/HorizontalPanelElement.cs
+++ b/Source/Editor/CustomEditors/Elements/Container/HorizontalPanelElement.cs
@@ -5,15 +5,15 @@ using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Elements
{
///
- /// The vertical panel element.
+ /// The horizontal panel element.
///
///
- public class VerticalPanelElement : LayoutElementsContainer
+ public class HorizontalPanelElement : LayoutElementsContainer
{
///
/// The panel.
///
- public readonly VerticalPanel Panel = new VerticalPanel();
+ public readonly HorizontalPanel Panel = new HorizontalPanel();
///
public override ContainerControl ContainerControl => Panel;
diff --git a/Source/Editor/CustomEditors/Elements/Container/VerticalPanelElement.cs b/Source/Editor/CustomEditors/Elements/Container/VerticalPanelElement.cs
index de015a371..2958cd337 100644
--- a/Source/Editor/CustomEditors/Elements/Container/VerticalPanelElement.cs
+++ b/Source/Editor/CustomEditors/Elements/Container/VerticalPanelElement.cs
@@ -5,15 +5,15 @@ using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Elements
{
///
- /// The horizontal panel element.
+ /// The vertical panel element.
///
///
- public class HorizontalPanelElement : LayoutElementsContainer
+ public class VerticalPanelElement : LayoutElementsContainer
{
///
/// The panel.
///
- public readonly HorizontalPanel Panel = new HorizontalPanel();
+ public readonly VerticalPanel Panel = new VerticalPanel();
///
public override ContainerControl ContainerControl => Panel;
diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs
index ae33599c1..6f645789a 100644
--- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs
+++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs
@@ -112,7 +112,7 @@ namespace FlaxEditor.CustomEditors
OnAddElement(element);
return element;
}
-
+
///
/// Adds new horizontal panel element.
///
@@ -690,6 +690,17 @@ namespace FlaxEditor.CustomEditors
return element;
}
+ ///
+ /// Adds custom element to the layout.
+ ///
+ /// The element.
+ public void AddElement(LayoutElement element)
+ {
+ if (element == null)
+ throw new ArgumentNullException();
+ OnAddElement(element);
+ }
+
///
/// Called when element is added to the layout.
///
diff --git a/Source/Editor/CustomEditors/Values/ListValueContainer.cs b/Source/Editor/CustomEditors/Values/ListValueContainer.cs
index f8e5dd26c..416b866cc 100644
--- a/Source/Editor/CustomEditors/Values/ListValueContainer.cs
+++ b/Source/Editor/CustomEditors/Values/ListValueContainer.cs
@@ -46,10 +46,7 @@ namespace FlaxEditor.CustomEditors
if (values.HasReferenceValue)
{
- var v = (IList)values.ReferenceValue;
-
- // Get the reference value if collections are the same size
- if (v != null && values.Count == v.Count)
+ if (values.ReferenceValue is IList v && values.Count == v.Count && v.Count > index)
{
_referenceValue = v[index];
_hasReferenceValue = true;
diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs
index 9808b427e..69c842820 100644
--- a/Source/Editor/Editor.Build.cs
+++ b/Source/Editor/Editor.Build.cs
@@ -59,6 +59,7 @@ public class Editor : EditorModule
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "PS4", "PLATFORM_TOOLS_PS4");
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "XboxScarlett", "PLATFORM_TOOLS_XBOX_SCARLETT");
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID");
+ AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Switch", "PLATFORM_TOOLS_SWITCH");
}
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX");
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index f09ff3332..b1f3691c9 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -752,10 +752,11 @@ namespace FlaxEditor
/// The collision data type.
/// The source model.
/// The source model LOD index.
+ /// The source model material slots mask. One bit per-slot. Can be sued to exclude particular material slots from collision cooking.
/// The convex mesh generation flags.
/// The convex mesh vertex limit. Use values in range [8;255]
/// True if failed, otherwise false.
- public static bool CookMeshCollision(string path, CollisionDataType type, Model model, int modelLodIndex = 0, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255)
+ public static bool CookMeshCollision(string path, CollisionDataType type, ModelBase model, int modelLodIndex = 0, uint materialSlotsMask = uint.MaxValue, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException(nameof(path));
@@ -764,7 +765,7 @@ namespace FlaxEditor
if (type == CollisionDataType.None)
throw new ArgumentException(nameof(type));
- return Internal_CookMeshCollision(path, type, FlaxEngine.Object.GetUnmanagedPtr(model), modelLodIndex, convexFlags, convexVertexLimit);
+ return Internal_CookMeshCollision(path, type, FlaxEngine.Object.GetUnmanagedPtr(model), modelLodIndex, materialSlotsMask, convexFlags, convexVertexLimit);
}
///
@@ -877,10 +878,12 @@ namespace FlaxEditor
/// Checks if can import asset with the given extension.
///
/// The file extension.
+ /// The output file extension (flax, json, etc.).
/// True if can import files with given extension, otherwise false.
- public static bool CanImport(string extension)
+ public static bool CanImport(string extension, out string outputExtension)
{
- return Internal_CanImport(extension);
+ outputExtension = Internal_CanImport(extension);
+ return outputExtension != null;
}
///
@@ -1344,7 +1347,7 @@ namespace FlaxEditor
internal static extern string Internal_GetShaderAssetSourceCode(IntPtr obj);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
+ internal static extern bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, uint materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_GetCollisionWires(IntPtr collisionData, out Vector3[] triangles, out int[] indices);
@@ -1368,7 +1371,7 @@ namespace FlaxEditor
internal static extern bool Internal_CreateVisualScript(string outputPath, string baseTypename);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_CanImport(string extension);
+ internal static extern string Internal_CanImport(string extension);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_CanExport(string path);
diff --git a/Source/Editor/GUI/EnumComboBox.cs b/Source/Editor/GUI/EnumComboBox.cs
index 8ceae0bb0..ed1c80cd6 100644
--- a/Source/Editor/GUI/EnumComboBox.cs
+++ b/Source/Editor/GUI/EnumComboBox.cs
@@ -29,7 +29,7 @@ namespace FlaxEditor.GUI
///
/// The cached value from the UI.
///
- protected int _cachedValue;
+ protected long _cachedValue;
///
/// True if has value cached, otherwise false.
@@ -54,7 +54,7 @@ namespace FlaxEditor.GUI
///
/// The value.
///
- public int Value;
+ public long Value;
///
/// Initializes a new instance of the struct.
@@ -62,7 +62,7 @@ namespace FlaxEditor.GUI
/// The name.
/// The tooltip.
/// The value.
- public Entry(string name, int value, string tooltip = null)
+ public Entry(string name, long value, string tooltip = null)
{
Name = name;
Tooltip = tooltip;
@@ -88,13 +88,13 @@ namespace FlaxEditor.GUI
public object EnumTypeValue
{
get => Enum.ToObject(_enumType, Value);
- set => Value = Convert.ToInt32(value);
+ set => Value = Convert.ToInt64(value);
}
///
/// Gets or sets the value.
///
- public int Value
+ public long Value
{
get => _cachedValue;
set
@@ -209,13 +209,13 @@ namespace FlaxEditor.GUI
///
protected void CacheValue()
{
- int value = 0;
+ long value = 0;
if (IsFlags)
{
var selection = Selection;
for (int i = 0; i < selection.Count; i++)
{
- int index = selection[i];
+ var index = selection[i];
value |= _entries[index].Value;
}
}
@@ -276,7 +276,7 @@ namespace FlaxEditor.GUI
tooltip = tooltipAttr.Text;
}
- entries.Add(new Entry(name, Convert.ToInt32(field.GetRawConstantValue()), tooltip));
+ entries.Add(new Entry(name, Convert.ToInt64(field.GetRawConstantValue()), tooltip));
}
}
@@ -295,9 +295,9 @@ namespace FlaxEditor.GUI
}
// Calculate value that will be set after change
- int valueAfter = 0;
+ long valueAfter = 0;
bool isSelected = _selectedIndices.Contains(index);
- int selectedValue = entries[index].Value;
+ long selectedValue = entries[index].Value;
for (int i = 0; i < _selectedIndices.Count; i++)
{
int selectedIndex = _selectedIndices[i];
diff --git a/Source/Editor/GUI/PlatformSelector.cs b/Source/Editor/GUI/PlatformSelector.cs
index 64448dfdc..cc7bc003a 100644
--- a/Source/Editor/GUI/PlatformSelector.cs
+++ b/Source/Editor/GUI/PlatformSelector.cs
@@ -89,6 +89,8 @@ namespace FlaxEditor.GUI
new PlatformData(PlatformType.PS4, icons.PS4Icon128, "PlayStation 4"),
new PlatformData(PlatformType.XboxScarlett, icons.XBoxScarletIcon128, "Xbox Scarlett"),
new PlatformData(PlatformType.Android, icons.AndroidIcon128, "Android"),
+ new PlatformData(PlatformType.Switch, icons.ColorWheel128, "Switch"),
+
};
const float IconSize = 48.0f;
diff --git a/Source/Editor/History/UndoActionObject.cs b/Source/Editor/History/UndoActionObject.cs
index cac710bff..a87637f4f 100644
--- a/Source/Editor/History/UndoActionObject.cs
+++ b/Source/Editor/History/UndoActionObject.cs
@@ -4,6 +4,8 @@ using System;
using System.Collections.Generic;
using FlaxEditor.Utilities;
using FlaxEngine;
+using Newtonsoft.Json;
+using JsonSerializer = FlaxEngine.Json.JsonSerializer;
namespace FlaxEditor.History
{
@@ -114,6 +116,8 @@ namespace FlaxEditor.History
public object TargetInstance;
}
+ internal static JsonSerializerSettings JsonSettings;
+
// For objects that cannot be referenced in undo action like: FlaxEngine.Object or SceneGraphNode we store them in DataStorage,
// otherwise here:
private readonly object TargetInstance;
@@ -177,6 +181,24 @@ namespace FlaxEditor.History
};
}
+ ///
+ public override DataStorage Data
+ {
+ protected set
+ {
+ // Inject objects typename serialization to prevent data type mismatch when loading from saved state
+ var settings = JsonSettings;
+ if (settings == null)
+ {
+ settings = JsonSerializer.CreateDefaultSettings(false);
+ settings.TypeNameHandling = TypeNameHandling.All;
+ JsonSettings = settings;
+ }
+ _data = JsonConvert.SerializeObject(value, Formatting.Indented, settings);
+ //Editor.Log(_data);
+ }
+ }
+
///
public override string ActionString { get; }
diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp
index 7b178b956..66f5a3c67 100644
--- a/Source/Editor/Managed/ManagedEditor.Internal.cpp
+++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp
@@ -521,13 +521,14 @@ public:
return AssetsImportingManager::Create(AssetsImportingManager::CreateVisualScriptTag, outputPath, &baseTypename);
}
- static bool CanImport(MonoString* extensionObj)
+ static MonoString* CanImport(MonoString* extensionObj)
{
String extension;
MUtils::ToString(extensionObj, extension);
if (extension.Length() > 0 && extension[0] == '.')
extension.Remove(0, 1);
- return AssetsImportingManager::GetImporter(extension) != nullptr;
+ const AssetImporter* importer = AssetsImportingManager::GetImporter(extension);
+ return importer ? MUtils::ToString(importer->ResultExtension) : nullptr;
}
static bool Import(MonoString* inputPathObj, MonoString* outputPathObj, void* arg)
@@ -715,7 +716,7 @@ public:
return str;
}
- static bool CookMeshCollision(MonoString* pathObj, CollisionDataType type, Model* modelObj, int32 modelLodIndex, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
+ static bool CookMeshCollision(MonoString* pathObj, CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, uint32 materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
#if COMPILE_WITH_PHYSICS_COOKING
CollisionCooking::Argument arg;
@@ -725,9 +726,9 @@ public:
arg.Type = type;
arg.Model = modelObj;
arg.ModelLodIndex = modelLodIndex;
+ arg.MaterialSlotsMask = materialSlotsMask;
arg.ConvexFlags = convexFlags;
arg.ConvexVertexLimit = convexVertexLimit;
-
return CreateCollisionData::CookMeshCollision(path, arg);
#else
LOG(Warning, "Collision cooking is disabled.");
@@ -743,8 +744,8 @@ public:
const auto& debugLines = collisionData->GetDebugLines();
const int32 linesCount = debugLines.Count() / 2;
- *triangles = mono_array_new(mono_domain_get(), StdTypesContainer::Instance()->Vector3Class->GetNative(), debugLines.Count());
- *indices = mono_array_new(mono_domain_get(), mono_get_int32_class(), linesCount * 3);
+ mono_gc_wbarrier_generic_store(triangles, (MonoObject*)mono_array_new(mono_domain_get(), StdTypesContainer::Instance()->Vector3Class->GetNative(), debugLines.Count()));
+ mono_gc_wbarrier_generic_store(indices, (MonoObject*)mono_array_new(mono_domain_get(), mono_get_int32_class(), linesCount * 3));
// Use one triangle per debug line
for (int32 i = 0; i < debugLines.Count(); i++)
diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs
index c90a386c1..ed1f69726 100644
--- a/Source/Editor/Modules/ContentDatabaseModule.cs
+++ b/Source/Editor/Modules/ContentDatabaseModule.cs
@@ -923,6 +923,7 @@ namespace FlaxEditor.Modules
Proxy.Add(new SkeletonMaskProxy());
Proxy.Add(new GameplayGlobalsProxy());
Proxy.Add(new VisualScriptProxy());
+ Proxy.Add(new LocalizedStringTableProxy());
Proxy.Add(new FileProxy());
Proxy.Add(new SpawnableJsonAssetProxy());
@@ -933,6 +934,7 @@ namespace FlaxEditor.Modules
Proxy.Add(new SettingsProxy(typeof(PhysicsSettings)));
Proxy.Add(new SettingsProxy(typeof(GraphicsSettings)));
Proxy.Add(new SettingsProxy(typeof(NavigationSettings)));
+ Proxy.Add(new SettingsProxy(typeof(LocalizationSettings)));
Proxy.Add(new SettingsProxy(typeof(BuildSettings)));
Proxy.Add(new SettingsProxy(typeof(InputSettings)));
Proxy.Add(new SettingsProxy(typeof(WindowsPlatformSettings)));
@@ -945,6 +947,9 @@ namespace FlaxEditor.Modules
if (typeXboxScarlettPlatformSettings != null)
Proxy.Add(new SettingsProxy(typeXboxScarlettPlatformSettings));
Proxy.Add(new SettingsProxy(typeof(AndroidPlatformSettings)));
+ var typeSwitchPlatformSettings = TypeUtils.GetManagedType(GameSettings.SwitchPlatformSettingsTypename);
+ if (typeSwitchPlatformSettings != null)
+ Proxy.Add(new SettingsProxy(typeSwitchPlatformSettings));
Proxy.Add(new SettingsProxy(typeof(AudioSettings)));
// Last add generic json (won't override other json proxies)
diff --git a/Source/Editor/Modules/ContentFindingModule.cs b/Source/Editor/Modules/ContentFindingModule.cs
index 9be15e0d1..45748f7f4 100644
--- a/Source/Editor/Modules/ContentFindingModule.cs
+++ b/Source/Editor/Modules/ContentFindingModule.cs
@@ -309,6 +309,7 @@ namespace FlaxEditor.Modules
{ "FlaxEditor.Content.Settings.InputSettings", "Settings" },
{ "FlaxEditor.Content.Settings.LayersAndTagsSettings", "Settings" },
{ "FlaxEditor.Content.Settings.NavigationSettings", "Settings" },
+ { "FlaxEditor.Content.Settings.LocalizationSettings", "Settings" },
{ "FlaxEditor.Content.Settings.PhysicsSettings", "Settings" },
{ "FlaxEditor.Content.Settings.TimeSettings", "Settings" },
{ "FlaxEditor.Content.Settings.UWPPlatformSettings", "Settings" },
diff --git a/Source/Editor/Modules/ContentImportingModule.cs b/Source/Editor/Modules/ContentImportingModule.cs
index d5ec4b69f..424868fd8 100644
--- a/Source/Editor/Modules/ContentImportingModule.cs
+++ b/Source/Editor/Modules/ContentImportingModule.cs
@@ -193,12 +193,10 @@ namespace FlaxEditor.Modules
var extension = System.IO.Path.GetExtension(inputPath) ?? string.Empty;
// Check if given file extension is a binary asset (.flax files) and can be imported by the engine
- bool isBinaryAsset = Editor.CanImport(extension);
- string outputExtension;
- if (isBinaryAsset)
+ bool isBuilt = Editor.CanImport(extension, out var outputExtension);
+ if (isBuilt)
{
- // Flax it up!
- outputExtension = ".flax";
+ outputExtension = '.' + outputExtension;
if (!targetLocation.CanHaveAssets)
{
@@ -234,7 +232,7 @@ namespace FlaxEditor.Modules
var shortName = System.IO.Path.GetFileNameWithoutExtension(inputPath);
var outputPath = System.IO.Path.Combine(targetLocation.Path, shortName + outputExtension);
- Import(inputPath, outputPath, isBinaryAsset, skipSettingsDialog, settings);
+ Import(inputPath, outputPath, isBuilt, skipSettingsDialog, settings);
}
///
@@ -243,10 +241,10 @@ namespace FlaxEditor.Modules
///
/// The input path.
/// The output path.
- /// True if output file is a binary asset.
+ /// True if use in-built importer (engine backend).
/// True if skip any popup dialogs showing for import options adjusting. Can be used when importing files from code.
/// Import settings to override. Use null to skip this value.
- private void Import(string inputPath, string outputPath, bool isBinaryAsset, bool skipSettingsDialog = false, object settings = null)
+ private void Import(string inputPath, string outputPath, bool isInBuilt, bool skipSettingsDialog = false, object settings = null)
{
lock (_requests)
{
@@ -254,7 +252,7 @@ namespace FlaxEditor.Modules
{
InputPath = inputPath,
OutputPath = outputPath,
- IsBinaryAsset = isBinaryAsset,
+ IsInBuilt = isInBuilt,
SkipSettingsDialog = skipSettingsDialog,
Settings = settings,
});
diff --git a/Source/Editor/Modules/SceneModule.cs b/Source/Editor/Modules/SceneModule.cs
index 6334225fd..86479b696 100644
--- a/Source/Editor/Modules/SceneModule.cs
+++ b/Source/Editor/Modules/SceneModule.cs
@@ -22,14 +22,25 @@ namespace FlaxEditor.Modules
///
public class ScenesRootNode : RootNode
{
+ private readonly Editor _editor;
+
+ ///
+ public ScenesRootNode()
+ {
+ _editor = Editor.Instance;
+ }
+
///
public override void Spawn(Actor actor, Actor parent)
{
- Editor.Instance.SceneEditing.Spawn(actor, parent);
+ _editor.SceneEditing.Spawn(actor, parent);
}
///
public override Undo Undo => Editor.Instance.Undo;
+
+ ///
+ public override List Selection => _editor.SceneEditing.Selection;
}
///
diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs
index a8eb0101d..ea42ee99b 100644
--- a/Source/Editor/Options/OptionsModule.cs
+++ b/Source/Editor/Options/OptionsModule.cs
@@ -150,7 +150,10 @@ namespace FlaxEditor.Options
private void Save()
{
// Update file
- Editor.SaveJsonAsset(_optionsFilePath, Options);
+ if (Editor.SaveJsonAsset(_optionsFilePath, Options))
+ {
+ MessageBox.Show(string.Format("Failed to save editor option to '{0}'. Ensure that directory exists and program has access to it.", _optionsFilePath), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
// Special case for editor analytics
var editorAnalyticsTrackingFile = Path.Combine(Editor.LocalCachePath, "noTracking");
diff --git a/Source/Editor/SceneGraph/ActorNode.cs b/Source/Editor/SceneGraph/ActorNode.cs
index 017cff3bf..6703c30a0 100644
--- a/Source/Editor/SceneGraph/ActorNode.cs
+++ b/Source/Editor/SceneGraph/ActorNode.cs
@@ -194,7 +194,7 @@ namespace FlaxEditor.SceneGraph
{
get
{
- var scene = _actor.Scene;
+ var scene = _actor ? _actor.Scene : null;
return scene != null ? SceneGraphFactory.FindNode(scene.ID) as SceneNode : null;
}
}
@@ -235,7 +235,6 @@ namespace FlaxEditor.SceneGraph
{
if (!(value is ActorNode))
throw new InvalidOperationException("ActorNode can have only ActorNode as a parent node.");
-
base.ParentNode = value;
}
}
@@ -289,6 +288,13 @@ namespace FlaxEditor.SceneGraph
{
}
+ ///
+ /// Action called after pasting actor in editor.
+ ///
+ public virtual void PostPaste()
+ {
+ }
+
///
protected override void OnParentChanged()
{
@@ -299,6 +305,7 @@ namespace FlaxEditor.SceneGraph
// (eg. we build new node for spawned actor and link it to the game)
if (_treeNode.Parent != null && !_treeNode.Parent.IsLayoutLocked)
{
+ _treeNode.IndexInParent = _actor.OrderInParent;
_treeNode.Parent.SortChildren();
// Update UI
diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs
index b4914941a..7f9e4ca70 100644
--- a/Source/Editor/SceneGraph/Actors/SplineNode.cs
+++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs
@@ -29,7 +29,7 @@ namespace FlaxEditor.SceneGraph.Actors
public override bool CanBeSelectedDirectly => true;
- public override bool CanDuplicate => true;
+ public override bool CanDuplicate => !Root?.Selection.Contains(ParentNode) ?? true;
public override bool CanDelete => true;
diff --git a/Source/Editor/SceneGraph/Actors/UIControlNode.cs b/Source/Editor/SceneGraph/Actors/UIControlNode.cs
index b3f8f5d09..c4f7d1c17 100644
--- a/Source/Editor/SceneGraph/Actors/UIControlNode.cs
+++ b/Source/Editor/SceneGraph/Actors/UIControlNode.cs
@@ -25,5 +25,20 @@ namespace FlaxEditor.SceneGraph.Actors
if (Actor is UIControl uiControl)
DebugDraw.DrawWireBox(uiControl.Bounds, Color.BlueViolet);
}
+
+ ///
+ public override void PostPaste()
+ {
+ base.PostPaste();
+
+ var control = ((UIControl)Actor).Control;
+ if (control != null)
+ {
+ if (control.Parent != null)
+ control.Parent.PerformLayout();
+ else
+ control.PerformLayout();
+ }
+ }
}
}
diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
index 7b4b35ed1..2c050c2e3 100644
--- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
+++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs
@@ -86,6 +86,10 @@ namespace FlaxEditor.SceneGraph.GUI
}
parent.SortChildren();
}
+ else if (Actor)
+ {
+ _orderInParent = Actor.OrderInParent;
+ }
}
internal void OnNameChanged()
diff --git a/Source/Editor/SceneGraph/RootNode.cs b/Source/Editor/SceneGraph/RootNode.cs
index 10af88489..485c656b1 100644
--- a/Source/Editor/SceneGraph/RootNode.cs
+++ b/Source/Editor/SceneGraph/RootNode.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
+using System.Collections.Generic;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
@@ -146,5 +147,10 @@ namespace FlaxEditor.SceneGraph
/// Gets the undo.
///
public abstract Undo Undo { get; }
+
+ ///
+ /// Gets the list of selected scene graph nodes in the editor context.
+ ///
+ public abstract List Selection { get; }
}
}
diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs
index 11c3b7709..bc8fe84aa 100644
--- a/Source/Editor/SceneGraph/SceneGraphNode.cs
+++ b/Source/Editor/SceneGraph/SceneGraphNode.cs
@@ -57,7 +57,9 @@ namespace FlaxEditor.SceneGraph
///
public virtual RootNode Root => ParentNode?.Root;
- ///
+ ///
+ /// Gets or sets the transform of the node.
+ ///
public abstract Transform Transform { get; set; }
///
@@ -110,18 +112,9 @@ namespace FlaxEditor.SceneGraph
{
if (parentNode != value)
{
- if (parentNode != null)
- {
- parentNode.ChildNodes.Remove(this);
- }
-
+ parentNode?.ChildNodes.Remove(this);
parentNode = value;
-
- if (parentNode != null)
- {
- parentNode.ChildNodes.Add(this);
- }
-
+ parentNode?.ChildNodes.Add(this);
OnParentChanged();
}
}
@@ -372,6 +365,7 @@ namespace FlaxEditor.SceneGraph
///
/// Gets or sets the node state.
///
+ [NoSerialize]
public virtual StateData State
{
get => throw new NotImplementedException();
diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp
index 00a93b66c..71323dc34 100644
--- a/Source/Editor/Scripting/ScriptsBuilder.cpp
+++ b/Source/Editor/Scripting/ScriptsBuilder.cpp
@@ -9,6 +9,7 @@
#include "Engine/Core/Types/StringBuilder.h"
#include "Engine/Debug/Exceptions/FileNotFoundException.h"
#include "Engine/Engine/Engine.h"
+#include "Engine/Engine/Globals.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/FileSystemWatcher.h"
#include "Engine/Threading/ThreadPool.h"
diff --git a/Source/Editor/Surface/Archetypes/Comparisons.cs b/Source/Editor/Surface/Archetypes/Comparisons.cs
index d77fd5ff7..44c49925d 100644
--- a/Source/Editor/Surface/Archetypes/Comparisons.cs
+++ b/Source/Editor/Surface/Archetypes/Comparisons.cs
@@ -137,7 +137,7 @@ namespace FlaxEditor.Surface.Archetypes
{
int* dataValues = (int*)dataPtr;
for (int i = 0; i < entries.Count; i++)
- dataValues[i] = entries[i].Value;
+ dataValues[i] = (int)entries[i].Value;
}
}
else
diff --git a/Source/Editor/Surface/Archetypes/Flow.cs b/Source/Editor/Surface/Archetypes/Flow.cs
index b8c426a5a..ffcfb719a 100644
--- a/Source/Editor/Surface/Archetypes/Flow.cs
+++ b/Source/Editor/Surface/Archetypes/Flow.cs
@@ -177,7 +177,7 @@ namespace FlaxEditor.Surface.Archetypes
{
int* dataValues = (int*)dataPtr;
for (int i = 0; i < entries.Count; i++)
- dataValues[i] = entries[i].Value;
+ dataValues[i] = (int)entries[i].Value;
}
}
else
diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs
index ae8bb2d21..4e017248a 100644
--- a/Source/Editor/Surface/Archetypes/Function.cs
+++ b/Source/Editor/Surface/Archetypes/Function.cs
@@ -576,7 +576,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < _parameters.Length; i++)
{
writer.Write(_parameters[i].Name); // Parameter name
- Utilities.Utils.WriteVariantType(writer, TypeUtils.GetType(_parameters[i].Type)); // Box type
+ Utilities.VariantUtils.WriteVariantType(writer, TypeUtils.GetType(_parameters[i].Type)); // Box type
}
SetValue(2, stream.ToArray());
}
@@ -594,7 +594,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < parametersCount; i++)
{
var parameterName = reader.ReadString(); // Parameter name
- var boxType = Utilities.Utils.ReadVariantType(reader); // Box type
+ var boxType = Utilities.VariantUtils.ReadVariantType(reader); // Box type
MakeBox(i + 1, parameterName, boxType);
}
}
@@ -778,14 +778,14 @@ namespace FlaxEditor.Surface.Archetypes
{
reader.ReadByte(); // Version
signature.IsStatic = reader.ReadBoolean(); // Is Static
- signature.ReturnType = Utilities.Utils.ReadVariantScriptType(reader); // Return type
+ signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return type
var parametersCount = reader.ReadInt32(); // Parameters count
signature.Params = parametersCount != 0 ? new SignatureParamInfo[parametersCount] : Utils.GetEmptyArray();
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref signature.Params[i];
param.Name = Utilities.Utils.ReadStr(reader, 11); // Parameter name
- param.Type = Utilities.Utils.ReadVariantScriptType(reader); // Parameter type
+ param.Type = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.IsOut = reader.ReadByte() != 0; // Is parameter out
}
}
@@ -799,14 +799,14 @@ namespace FlaxEditor.Surface.Archetypes
{
reader.ReadByte(); // Version
signature.IsStatic = reader.ReadBoolean(); // Is Static
- signature.ReturnType = Utilities.Utils.ReadVariantScriptType(reader); // Return type
+ signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return type
var parametersCount = reader.ReadInt32(); // Parameters count
signature.Params = parametersCount != 0 ? new SignatureParamInfo[parametersCount] : Utils.GetEmptyArray();
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref signature.Params[i];
param.Name = reader.ReadString(); // Parameter name
- param.Type = Utilities.Utils.ReadVariantScriptType(reader); // Parameter type
+ param.Type = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.IsOut = reader.ReadByte() != 0; // Is parameter out
}
}
@@ -823,13 +823,13 @@ namespace FlaxEditor.Surface.Archetypes
{
writer.Write((byte)4); // Version
writer.Write(methodInfo.IsStatic); // Is Static
- Utilities.Utils.WriteVariantType(writer, methodInfo.ValueType); // Return type
+ Utilities.VariantUtils.WriteVariantType(writer, methodInfo.ValueType); // Return type
writer.Write(parameters.Length); // Parameters count
for (int i = 0; i < parameters.Length; i++)
{
ref var param = ref parameters[i];
Utilities.Utils.WriteStr(writer, param.Name, 11); // Parameter name
- Utilities.Utils.WriteVariantType(writer, param.Type); // Parameter type
+ Utilities.VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
writer.Write((byte)(param.IsOut ? 1 : 0)); // Is parameter out
}
return stream.ToArray();
@@ -1434,14 +1434,14 @@ namespace FlaxEditor.Surface.Archetypes
if (_signature.IsVirtual)
flags |= Flags.Virtual;
writer.Write((byte)flags); // Flags
- Utilities.Utils.WriteVariantType(writer, _signature.ReturnType); // Return Type
+ Utilities.VariantUtils.WriteVariantType(writer, _signature.ReturnType); // Return Type
var parametersCount = _signature.Parameters?.Length ?? 0;
writer.Write(parametersCount); // Parameters count
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref _signature.Parameters[i];
Utilities.Utils.WriteStrAnsi(writer, param.Name, 13); // Parameter name
- Utilities.Utils.WriteVariantType(writer, param.Type); // Parameter type
+ Utilities.VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
writer.Write((byte)0); // Is parameter out
writer.Write((byte)0); // Has default value
}
@@ -1470,13 +1470,13 @@ namespace FlaxEditor.Surface.Archetypes
var flags = (Flags)reader.ReadByte(); // Flags
_signature.IsStatic = (flags & Flags.Static) == Flags.Static;
_signature.IsVirtual = (flags & Flags.Virtual) == Flags.Virtual;
- _signature.ReturnType = Utilities.Utils.ReadVariantScriptType(reader); // Return Type
+ _signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return Type
var parametersCount = reader.ReadInt32(); // Parameters count
_signature.Parameters = new Parameter[parametersCount];
for (int i = 0; i < parametersCount; i++)
{
var paramName = Utilities.Utils.ReadStrAnsi(reader, 13); // Parameter name
- var paramType = Utilities.Utils.ReadVariantScriptType(reader); // Parameter type
+ var paramType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
var isOut = reader.ReadByte() != 0; // Is parameter out
var hasDefaultValue = reader.ReadByte() != 0; // Has default value
_signature.Parameters[i] = new Parameter
diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs
index 95f503338..0e9fa4ff0 100644
--- a/Source/Editor/Surface/Archetypes/Packing.cs
+++ b/Source/Editor/Surface/Archetypes/Packing.cs
@@ -171,7 +171,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < fieldsLength; i++)
{
Utilities.Utils.WriteStr(writer, fields[i].Name, 11); // Field type
- Utilities.Utils.WriteVariantType(writer, fields[i].ValueType); // Field type
+ Utilities.VariantUtils.WriteVariantType(writer, fields[i].ValueType); // Field type
}
Values[1] = stream.ToArray();
}
@@ -188,7 +188,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < fieldsLength; i++)
{
var fieldName = Utilities.Utils.ReadStr(reader, 11); // Field name
- var fieldType = Utilities.Utils.ReadVariantType(reader); // Field type
+ var fieldType = Utilities.VariantUtils.ReadVariantType(reader); // Field type
MakeBox(i + 1, fieldName, new ScriptType(fieldType));
}
}
diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs
index 81c1e5bf4..005af20cc 100644
--- a/Source/Editor/Surface/Archetypes/Tools.cs
+++ b/Source/Editor/Surface/Archetypes/Tools.cs
@@ -672,6 +672,24 @@ namespace FlaxEditor.Surface.Archetypes
}
}
+ private class ThisNode : SurfaceNode
+ {
+ ///
+ public ThisNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
+ : base(id, context, nodeArch, groupArch)
+ {}
+
+ ///
+ public override void OnLoaded()
+ {
+ base.OnLoaded();
+ var vss = (VisualScriptSurface)this.Context.Surface;
+ var type = TypeUtils.GetType(vss.Script.ScriptTypeName);
+ var box = (OutputBox)GetBox(0);
+ box.CurrentType = type ? type : new ScriptType(typeof(VisualScript));
+ }
+ }
+
private class AssetReferenceNode : SurfaceNode
{
///
@@ -1326,9 +1344,9 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Platform Switch",
Description = "Gets the input value based on the runtime-platform type",
Flags = NodeFlags.AllGraphs,
- Size = new Vector2(220, 160),
+ Size = new Vector2(220, 180),
ConnectionsHints = ConnectionsHint.Value,
- IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8 },
+ IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
DependentBoxes = new[] { 0 },
Elements = new[]
{
@@ -1341,6 +1359,7 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Input(5, "PlayStation 4", true, null, 6),
NodeElementArchetype.Factory.Input(6, "Xbox Scarlett", true, null, 7),
NodeElementArchetype.Factory.Input(7, "Android", true, null, 8),
+ NodeElementArchetype.Factory.Input(8, "Switch", true, null, 9),
}
},
new NodeArchetype
@@ -1365,6 +1384,7 @@ namespace FlaxEditor.Surface.Archetypes
{
TypeID = 19,
Title = "This Instance",
+ Create = (id, context, arch, groupArch) => new ThisNode(id, context, arch, groupArch),
Description = "Gets the reference to this script object instance (self).",
Flags = NodeFlags.VisualScriptGraph,
Size = new Vector2(140, 20),
diff --git a/Source/Editor/Surface/Elements/EnumValue.cs b/Source/Editor/Surface/Elements/EnumValue.cs
index 9513abd78..dbd1620ab 100644
--- a/Source/Editor/Surface/Elements/EnumValue.cs
+++ b/Source/Editor/Surface/Elements/EnumValue.cs
@@ -38,7 +38,7 @@ namespace FlaxEditor.Surface.Elements
///
protected override void OnValueChanged()
{
- if ((int)ParentNode.Values[Archetype.ValueIndex] != Value)
+ if ((int)ParentNode.Values[Archetype.ValueIndex] != (int)Value)
{
// Edit value
ParentNode.SetValue(Archetype.ValueIndex, Value);
diff --git a/Source/Editor/Undo/Actions/PasteActorsAction.cs b/Source/Editor/Undo/Actions/PasteActorsAction.cs
index c65aa6bf0..c77feb71d 100644
--- a/Source/Editor/Undo/Actions/PasteActorsAction.cs
+++ b/Source/Editor/Undo/Actions/PasteActorsAction.cs
@@ -140,22 +140,27 @@ namespace FlaxEditor.Actions
for (int i = 0; i < nodeParents.Count; i++)
{
- // Fix name collisions (only for parents)
var node = nodeParents[i];
var parent = node.Actor?.Parent;
if (parent != null)
{
+ // Fix name collisions
string name = node.Name;
Actor[] children = parent.Children;
if (children.Any(x => x.Name == name))
{
- // Generate new name
node.Actor.Name = StringUtils.IncrementNameNumber(name, x => children.All(y => y.Name != x));
}
}
Editor.Instance.Scene.MarkSceneEdited(node.ParentScene);
}
+
+ for (int i = 0; i < nodeParents.Count; i++)
+ {
+ var node = nodeParents[i];
+ node.PostPaste();
+ }
}
///
diff --git a/Source/Editor/Undo/UndoActionBase.cs b/Source/Editor/Undo/UndoActionBase.cs
index 5de052a45..6648096c6 100644
--- a/Source/Editor/Undo/UndoActionBase.cs
+++ b/Source/Editor/Undo/UndoActionBase.cs
@@ -4,7 +4,6 @@ using System;
using FlaxEditor.SceneGraph;
using FlaxEngine;
using Newtonsoft.Json;
-using JsonSerializer = FlaxEngine.Json.JsonSerializer;
namespace FlaxEditor
{
@@ -28,7 +27,6 @@ namespace FlaxEditor
var id = Guid.Parse((string)reader.Value);
return SceneGraphFactory.FindNode(id);
}
-
return null;
}
@@ -56,19 +54,11 @@ namespace FlaxEditor
///
/// Gets or sets the serialized undo data.
///
- ///
- /// The data.
- ///
[NoSerialize]
- public TData Data
+ public virtual TData Data
{
- get => JsonConvert.DeserializeObject(_data, JsonSerializer.Settings);
- protected set => _data = JsonConvert.SerializeObject(value, Formatting.None, JsonSerializer.Settings);
- /*protected set
- {
- _data = JsonConvert.SerializeObject(value, Formatting.Indented, JsonSerializer.Settings);
- Debug.Info(_data);
- }*/
+ get => JsonConvert.DeserializeObject(_data, FlaxEngine.Json.JsonSerializer.Settings);
+ protected set => _data = JsonConvert.SerializeObject(value, Formatting.None, FlaxEngine.Json.JsonSerializer.Settings);
}
///
diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp
index aa26388c1..85d409b92 100644
--- a/Source/Editor/Utilities/EditorUtilities.cpp
+++ b/Source/Editor/Utilities/EditorUtilities.cpp
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "EditorUtilities.h"
+#include "Engine/Engine/Globals.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Core/Log.h"
diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs
index b5c835d70..3eab59f80 100644
--- a/Source/Editor/Utilities/Utils.cs
+++ b/Source/Editor/Utilities/Utils.cs
@@ -2,15 +2,15 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
+using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.GUI.Tree;
using FlaxEditor.SceneGraph;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
-using Newtonsoft.Json;
namespace FlaxEditor.Utilities
{
@@ -29,46 +29,6 @@ namespace FlaxEditor.Utilities
"PB"
};
- internal enum VariantType
- {
- Null = 0,
- Void,
-
- Bool,
- Int,
- Uint,
- Int64,
- Uint64,
- Float,
- Double,
- Pointer,
-
- String,
- Object,
- Structure,
- Asset,
- Blob,
- Enum,
-
- Vector2,
- Vector3,
- Vector4,
- Color,
- Guid,
- BoundingBox,
- BoundingSphere,
- Quaternion,
- Transform,
- Rectangle,
- Ray,
- Matrix,
-
- Array,
- Dictionary,
- ManagedObject,
- Typename,
- }
-
///
/// The name of the Flax Engine C# assembly name.
///
@@ -399,151 +359,6 @@ namespace FlaxEditor.Utilities
stream.Write(bytes);
}
- internal static VariantType ToVariantType(this Type type)
- {
- VariantType variantType;
- if (type == null)
- variantType = VariantType.Null;
- else if (type == typeof(void))
- variantType = VariantType.Void;
- else if (type == typeof(bool))
- variantType = VariantType.Bool;
- else if (type == typeof(int))
- variantType = VariantType.Int;
- else if (type == typeof(uint))
- variantType = VariantType.Uint;
- else if (type == typeof(long))
- variantType = VariantType.Int64;
- else if (type == typeof(ulong))
- variantType = VariantType.Uint64;
- else if (type.IsEnum)
- variantType = VariantType.Enum;
- else if (type == typeof(float))
- variantType = VariantType.Float;
- else if (type == typeof(double))
- variantType = VariantType.Double;
- else if (type == typeof(IntPtr))
- variantType = VariantType.Pointer;
- else if (type == typeof(string))
- variantType = VariantType.String;
- else if (type == typeof(Type) || type == typeof(ScriptType))
- variantType = VariantType.Typename;
- else if (typeof(Asset).IsAssignableFrom(type))
- variantType = VariantType.Asset;
- else if (typeof(FlaxEngine.Object).IsAssignableFrom(type))
- variantType = VariantType.Object;
- else if (type == typeof(BoundingBox))
- variantType = VariantType.BoundingBox;
- else if (type == typeof(Transform))
- variantType = VariantType.Transform;
- else if (type == typeof(Ray))
- variantType = VariantType.Ray;
- else if (type == typeof(Matrix))
- variantType = VariantType.Matrix;
- else if (type == typeof(Vector2))
- variantType = VariantType.Vector2;
- else if (type == typeof(Vector3))
- variantType = VariantType.Vector3;
- else if (type == typeof(Vector4))
- variantType = VariantType.Vector4;
- else if (type == typeof(Color))
- variantType = VariantType.Color;
- else if (type == typeof(Guid))
- variantType = VariantType.Guid;
- else if (type == typeof(Quaternion))
- variantType = VariantType.Quaternion;
- else if (type == typeof(Rectangle))
- variantType = VariantType.Rectangle;
- else if (type == typeof(BoundingSphere))
- variantType = VariantType.BoundingSphere;
- else if (type.IsValueType)
- variantType = VariantType.Structure;
- else if (type == typeof(byte[]))
- variantType = VariantType.Blob;
- else if (type == typeof(object[]))
- variantType = VariantType.Array;
- else if (type == typeof(Dictionary
/// Material resource name
- TerrainMaterialShader(const String& name)
+ TerrainMaterialShader(const StringView& name)
: MaterialShader(name)
{
}
diff --git a/Source/Engine/Graphics/Materials/VolumeParticleMaterialShader.h b/Source/Engine/Graphics/Materials/VolumeParticleMaterialShader.h
index e38944342..5cf69fecb 100644
--- a/Source/Engine/Graphics/Materials/VolumeParticleMaterialShader.h
+++ b/Source/Engine/Graphics/Materials/VolumeParticleMaterialShader.h
@@ -19,7 +19,7 @@ public:
/// Init
///
/// Material resource name
- VolumeParticleMaterialShader(const String& name)
+ VolumeParticleMaterialShader(const StringView& name)
: MaterialShader(name)
{
}
diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp
index 5d7f712a8..dddbd6a3e 100644
--- a/Source/Engine/Graphics/Models/Mesh.cpp
+++ b/Source/Engine/Graphics/Models/Mesh.cpp
@@ -5,6 +5,8 @@
#include "Engine/Content/Assets/Material.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Graphics/GPUContext.h"
+#include "Engine/Graphics/GPUDevice.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Serialization/MemoryReadStream.h"
@@ -131,22 +133,29 @@ namespace
}
}
+bool Mesh::HasVertexColors() const
+{
+ return _vertexBuffers[2] != nullptr && _vertexBuffers[2]->IsAllocated();
+}
+
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices)
{
+ auto model = (Model*)_model;
+
Unload();
// Setup GPU resources
- _model->LODs[_lodIndex]._verticesCount -= _vertices;
+ model->LODs[_lodIndex]._verticesCount -= _vertices;
const bool failed = Load(vertexCount, triangleCount, vb0, vb1, vb2, ib, use16BitIndices);
if (!failed)
{
- _model->LODs[_lodIndex]._verticesCount += _vertices;
+ model->LODs[_lodIndex]._verticesCount += _vertices;
// Calculate mesh bounds
SetBounds(BoundingBox::FromPoints((Vector3*)vb0, vertexCount));
// Send event (actors using this model can update bounds, etc.)
- _model->onLoaded();
+ model->onLoaded();
}
return failed;
@@ -214,17 +223,6 @@ Mesh::~Mesh()
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
}
-void Mesh::SetMaterialSlotIndex(int32 value)
-{
- if (value < 0 || value >= _model->MaterialSlots.Count())
- {
- LOG(Warning, "Cannot set mesh material slot to {0} while model has {1} slots.", value, _model->MaterialSlots.Count());
- return;
- }
-
- _materialSlotIndex = value;
-}
-
bool Mesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* vb1, void* vb2, void* ib, bool use16BitIndexBuffer)
{
// Cache data
@@ -443,7 +441,7 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
// TODO: cache vertexOffset within the model LOD per-mesh
uint32 vertexOffset = 0;
for (int32 meshIndex = 0; meshIndex < _index; meshIndex++)
- vertexOffset += _model->LODs[_lodIndex].Meshes[meshIndex].GetVertexCount();
+ vertexOffset += ((Model*)_model)->LODs[_lodIndex].Meshes[meshIndex].GetVertexCount();
drawCall.Geometry.VertexBuffers[2] = info.VertexColors[_lodIndex];
drawCall.Geometry.VertexBuffersOffsets[2] = vertexOffset * sizeof(VB2ElementType);
}
@@ -464,7 +462,7 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals);
}
-bool Mesh::ExtractData(MeshBufferType type, BytesContainer& result) const
+bool Mesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const
{
GPUBuffer* buffer = nullptr;
switch (type)
@@ -485,7 +483,7 @@ bool Mesh::ExtractData(MeshBufferType type, BytesContainer& result) const
return buffer && buffer->DownloadData(result);
}
-Task* Mesh::ExtractDataAsync(MeshBufferType type, BytesContainer& result) const
+Task* Mesh::DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const
{
GPUBuffer* buffer = nullptr;
switch (type)
@@ -506,6 +504,15 @@ Task* Mesh::ExtractDataAsync(MeshBufferType type, BytesContainer& result) const
return buffer ? buffer->DownloadDataAsync(result) : nullptr;
}
+bool Mesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result) const
+{
+#if !BUILD_RELEASE
+ // TODO: implement this
+ LOG(Error, "Mesh::DownloadDataCPU not implemented.");
+#endif
+ return true;
+}
+
ScriptingObject* Mesh::GetParentModel()
{
return _model;
@@ -661,7 +668,7 @@ bool Mesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI)
// TODO: support reusing the input memory buffer to perform a single copy from staging buffer to the input CPU buffer
BytesContainer data;
- auto task = mesh->ExtractDataAsync(bufferType, data);
+ auto task = mesh->DownloadDataGPUAsync(bufferType, data);
if (task == nullptr)
return true;
diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h
index 83c37c530..ca5174f7b 100644
--- a/Source/Engine/Graphics/Models/Mesh.h
+++ b/Source/Engine/Graphics/Models/Mesh.h
@@ -2,38 +2,29 @@
#pragma once
-#include "Engine/Core/Math/BoundingBox.h"
-#include "Engine/Core/Math/BoundingSphere.h"
-#include "Engine/Scripting/ScriptingObject.h"
-#include "Engine/Renderer/RenderList.h"
-#include "Engine/Graphics/RenderTask.h"
+#include "MeshBase.h"
#include "ModelInstanceEntry.h"
#include "Config.h"
#include "Types.h"
+#include "Engine/Level/Types.h"
#if USE_PRECISE_MESH_INTERSECTS
#include "CollisionProxy.h"
#endif
+struct GeometryDrawStateData;
+class Lightmap;
class GPUBuffer;
///
/// Represents part of the model that is made of vertices and can be rendered using custom material and transformation.
///
-API_CLASS(NoSpawn) class FLAXENGINE_API Mesh : public PersistentScriptingObject
+API_CLASS(NoSpawn) class FLAXENGINE_API Mesh : public MeshBase
{
-DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(Mesh, PersistentScriptingObject);
+DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(Mesh, MeshBase);
protected:
-
- Model* _model;
int32 _index;
int32 _lodIndex;
- int32 _materialSlotIndex;
- bool _use16BitIndexBuffer;
bool _hasLightmapUVs;
- BoundingBox _box;
- BoundingSphere _sphere;
- uint32 _vertices;
- uint32 _triangles;
GPUBuffer* _vertexBuffers[3];
GPUBuffer* _indexBuffer;
#if USE_PRECISE_MESH_INTERSECTS
@@ -41,7 +32,6 @@ protected:
#endif
public:
-
Mesh(const Mesh& other)
: Mesh()
{
@@ -56,13 +46,12 @@ public:
~Mesh();
public:
-
///
/// Gets the model owning this mesh.
///
FORCE_INLINE Model* GetModel() const
{
- return _model;
+ return (Model*)_model;
}
///
@@ -81,35 +70,6 @@ public:
return _index;
}
- ///
- /// Gets the index of the material slot to use during this mesh rendering.
- ///
- API_PROPERTY() FORCE_INLINE int32 GetMaterialSlotIndex() const
- {
- return _materialSlotIndex;
- }
-
- ///
- /// Sets the index of the material slot to use during this mesh rendering.
- ///
- API_PROPERTY() void SetMaterialSlotIndex(int32 value);
-
- ///
- /// Gets the triangle count.
- ///
- API_PROPERTY() FORCE_INLINE int32 GetTriangleCount() const
- {
- return _triangles;
- }
-
- ///
- /// Gets the vertex count.
- ///
- API_PROPERTY() FORCE_INLINE int32 GetVertexCount() const
- {
- return _vertices;
- }
-
///
/// Gets the index buffer.
///
@@ -138,23 +98,11 @@ public:
return _vertexBuffers[0] != nullptr;
}
- ///
- /// Determines whether this mesh is using 16 bit index buffer, otherwise it's 32 bit.
- ///
- /// True if this mesh is using 16 bit index buffer, otherwise 32 bit index buffer.
- API_PROPERTY() FORCE_INLINE bool Use16BitIndexBuffer() const
- {
- return _use16BitIndexBuffer;
- }
-
///
/// Determines whether this mesh has a vertex colors buffer.
///
/// True if this mesh has a vertex colors buffers.
- API_PROPERTY() FORCE_INLINE bool HasVertexColors() const
- {
- return _vertexBuffers[2] != nullptr && _vertexBuffers[2]->IsAllocated();
- }
+ API_PROPERTY() bool HasVertexColors() const;
///
/// Determines whether this mesh contains valid lightmap texture coordinates data.
@@ -165,34 +113,6 @@ public:
return _hasLightmapUVs;
}
- ///
- /// Sets the mesh bounds.
- ///
- /// The bounding box.
- void SetBounds(const BoundingBox& box)
- {
- _box = box;
- BoundingSphere::FromBox(box, _sphere);
- }
-
- ///
- /// Gets the box.
- ///
- /// The bounding box.
- API_PROPERTY() FORCE_INLINE const BoundingBox& GetBox() const
- {
- return _box;
- }
-
- ///
- /// Gets the sphere.
- ///
- /// The bounding sphere.
- API_PROPERTY() FORCE_INLINE const BoundingSphere& GetSphere() const
- {
- return _sphere;
- }
-
#if USE_PRECISE_MESH_INTERSECTS
///
@@ -207,7 +127,6 @@ public:
#endif
public:
-
///
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
///
@@ -286,7 +205,6 @@ public:
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr);
public:
-
///
/// Updates the model mesh index buffer (used by the virtual models created with Init rather than Load).
///
@@ -319,7 +237,6 @@ public:
bool UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices);
public:
-
///
/// Initializes instance of the class.
///
@@ -351,7 +268,6 @@ public:
void Unload();
public:
-
///
/// Determines if there is an intersection between the mesh and a ray in given world
///
@@ -372,7 +288,6 @@ public:
}
public:
-
///
/// Gets the draw call geometry for this mesh. Sets the index and vertex buffers.
///
@@ -472,25 +387,12 @@ public:
void Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const;
public:
-
- ///
- /// Extract mesh buffer data (cannot be called from the main thread!).
- ///
- /// Buffer type
- /// The result data
- /// True if failed, otherwise false
- bool ExtractData(MeshBufferType type, BytesContainer& result) const;
-
- ///
- /// Extracts mesh buffer data in the async task.
- ///
- /// Buffer type
- /// The result data
- /// Created async task used to gather the buffer data.
- Task* ExtractDataAsync(MeshBufferType type, BytesContainer& result) const;
+ // [MeshBase]
+ bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override;
+ Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const override;
+ bool DownloadDataCPU(MeshBufferType type, BytesContainer& result) const override;
private:
-
// Internal bindings
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
API_FUNCTION(NoProxy) bool UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj);
diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h
new file mode 100644
index 000000000..bedb57c90
--- /dev/null
+++ b/Source/Engine/Graphics/Models/MeshBase.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Engine/Core/Math/BoundingBox.h"
+#include "Engine/Core/Math/BoundingSphere.h"
+#include "Engine/Core/Types/DataContainer.h"
+#include "Engine/Graphics/Models/Types.h"
+#include "Engine/Scripting/ScriptingObject.h"
+
+class Task;
+class ModelBase;
+
+///
+/// Base class for model resources meshes.
+///
+API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API MeshBase : public PersistentScriptingObject
+{
+DECLARE_SCRIPTING_TYPE_MINIMAL(MeshBase);
+protected:
+
+ ModelBase* _model;
+ bool _use16BitIndexBuffer;
+ BoundingBox _box;
+ BoundingSphere _sphere;
+ uint32 _vertices;
+ uint32 _triangles;
+ int32 _materialSlotIndex;
+
+ explicit MeshBase(const SpawnParams& params)
+ : PersistentScriptingObject(params)
+ {
+ }
+
+public:
+
+ ///
+ /// Gets the model owning this mesh.
+ ///
+ FORCE_INLINE ModelBase* GetModelBase() const
+ {
+ return _model;
+ }
+
+ ///
+ /// Gets the triangle count.
+ ///
+ /// The triangles
+ API_PROPERTY() FORCE_INLINE int32 GetTriangleCount() const
+ {
+ return _triangles;
+ }
+
+ ///
+ /// Gets the vertex count.
+ ///
+ /// The vertices
+ API_PROPERTY() FORCE_INLINE int32 GetVertexCount() const
+ {
+ return _vertices;
+ }
+
+ ///
+ /// Gets the box.
+ ///
+ /// The bounding box.
+ API_PROPERTY() FORCE_INLINE const BoundingBox& GetBox() const
+ {
+ return _box;
+ }
+
+ ///
+ /// Gets the sphere.
+ ///
+ /// The bounding sphere.
+ API_PROPERTY() FORCE_INLINE const BoundingSphere& GetSphere() const
+ {
+ return _sphere;
+ }
+
+ ///
+ /// Determines whether this mesh is using 16 bit index buffer, otherwise it's 32 bit.
+ ///
+ /// True if this mesh is using 16 bit index buffer, otherwise 32 bit index buffer.
+ API_PROPERTY() FORCE_INLINE bool Use16BitIndexBuffer() const
+ {
+ return _use16BitIndexBuffer;
+ }
+
+ ///
+ /// Gets the index of the material slot to use during this mesh rendering.
+ ///
+ API_PROPERTY() FORCE_INLINE int32 GetMaterialSlotIndex() const
+ {
+ return _materialSlotIndex;
+ }
+
+ ///
+ /// Sets the index of the material slot to use during this mesh rendering.
+ ///
+ API_PROPERTY() void SetMaterialSlotIndex(int32 value);
+
+ ///
+ /// Sets the mesh bounds.
+ ///
+ /// The bounding box.
+ void SetBounds(const BoundingBox& box);
+
+public:
+
+ ///
+ /// Extract mesh buffer data from GPU. Cannot be called from the main thread.
+ ///
+ /// Buffer type
+ /// The result data
+ /// True if failed, otherwise false
+ virtual bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const = 0;
+
+ ///
+ /// Extracts mesh buffer data from GPU in the async task.
+ ///
+ /// Buffer type
+ /// The result data
+ /// Created async task used to gather the buffer data.
+ virtual Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const = 0;
+
+ ///
+ /// Extract mesh buffer data from CPU. Cached internally.
+ ///
+ /// Buffer type
+ /// The result data
+ /// True if failed, otherwise false
+ virtual bool DownloadDataCPU(MeshBufferType type, BytesContainer& result) const = 0;
+};
diff --git a/Source/Engine/Graphics/Models/ModelData.Tool.cpp b/Source/Engine/Graphics/Models/ModelData.Tool.cpp
index 8439a30d1..8faa9dcaa 100644
--- a/Source/Engine/Graphics/Models/ModelData.Tool.cpp
+++ b/Source/Engine/Graphics/Models/ModelData.Tool.cpp
@@ -6,6 +6,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/Utilities.h"
#include "Engine/Core/Types/DateTime.h"
+#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Collections/BitArray.h"
#include "Engine/Tools/ModelTool/ModelTool.h"
#include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h"
diff --git a/Source/Engine/Graphics/Models/SkeletonData.h b/Source/Engine/Graphics/Models/SkeletonData.h
index 5f336d8be..3e5700304 100644
--- a/Source/Engine/Graphics/Models/SkeletonData.h
+++ b/Source/Engine/Graphics/Models/SkeletonData.h
@@ -5,6 +5,8 @@
#include "Engine/Core/Math/Transform.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Types/String.h"
+#include "Engine/Core/Types/StringView.h"
+#include "Engine/Core/Collections/Array.h"
///
/// Describes a single skeleton node data. Used by the runtime.
diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp
index 9e26867ab..f2d1134d9 100644
--- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp
+++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp
@@ -4,6 +4,8 @@
#include "ModelInstanceEntry.h"
#include "Engine/Content/Assets/Material.h"
#include "Engine/Content/Assets/SkinnedModel.h"
+#include "Engine/Graphics/GPUDevice.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Serialization/MemoryReadStream.h"
@@ -34,17 +36,6 @@ SkinnedMesh::~SkinnedMesh()
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
}
-void SkinnedMesh::SetMaterialSlotIndex(int32 value)
-{
- if (value < 0 || value >= _model->MaterialSlots.Count())
- {
- LOG(Warning, "Cannot set mesh material slot to {0} while model has {1} slots.", value, _model->MaterialSlots.Count());
- return;
- }
-
- _materialSlotIndex = value;
-}
-
bool SkinnedMesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* ib, bool use16BitIndexBuffer)
{
// Cache data
@@ -101,6 +92,8 @@ void SkinnedMesh::Unload()
bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices)
{
+ auto model = (SkinnedModel*)_model;
+
// Setup GPU resources
const bool failed = Load(vertexCount, triangleCount, vb, ib, use16BitIndices);
if (!failed)
@@ -109,7 +102,7 @@ bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0Skinne
SetBounds(BoundingBox::FromPoints((Vector3*)vb, vertexCount));
// Send event (actors using this model can update bounds, etc.)
- _model->onLoaded();
+ model->onLoaded();
}
return failed;
@@ -218,7 +211,7 @@ bool SkinnedMesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) c
return buffer && buffer->DownloadData(result);
}
-Task* SkinnedMesh::DownloadDataAsyncGPU(MeshBufferType type, BytesContainer& result) const
+Task* SkinnedMesh::DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const
{
GPUBuffer* buffer = nullptr;
switch (type)
@@ -535,7 +528,7 @@ bool SkinnedMesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 type
{
// Get data from GPU
// TODO: support reusing the input memory buffer to perform a single copy from staging buffer to the input CPU buffer
- auto task = mesh->DownloadDataAsyncGPU(bufferType, data);
+ auto task = mesh->DownloadDataGPUAsync(bufferType, data);
if (task == nullptr)
return true;
task->Start();
diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h
index bbdf38014..eb854189e 100644
--- a/Source/Engine/Graphics/Models/SkinnedMesh.h
+++ b/Source/Engine/Graphics/Models/SkinnedMesh.h
@@ -2,34 +2,25 @@
#pragma once
-#include "Engine/Core/Math/BoundingBox.h"
-#include "Engine/Core/Math/BoundingSphere.h"
-#include "Engine/Scripting/ScriptingObject.h"
-#include "Engine/Renderer/RenderList.h"
-#include "Engine/Graphics/RenderTask.h"
-#include "ModelInstanceEntry.h"
+#include "MeshBase.h"
#include "Types.h"
#include "BlendShape.h"
+struct GeometryDrawStateData;
+struct RenderContext;
class GPUBuffer;
+class SkinnedMeshDrawData;
///
/// Represents part of the skinned model that is made of vertices and can be rendered using custom material, transformation and skeleton bones hierarchy.
///
-API_CLASS(NoSpawn) class FLAXENGINE_API SkinnedMesh : public PersistentScriptingObject
+API_CLASS(NoSpawn) class FLAXENGINE_API SkinnedMesh : public MeshBase
{
-DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(SkinnedMesh, PersistentScriptingObject);
+DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(SkinnedMesh, MeshBase);
protected:
- SkinnedModel* _model;
int32 _index;
int32 _lodIndex;
- int32 _materialSlotIndex;
- bool _use16BitIndexBuffer;
- BoundingBox _box;
- BoundingSphere _sphere;
- uint32 _vertices;
- uint32 _triangles;
GPUBuffer* _vertexBuffer;
GPUBuffer* _indexBuffer;
mutable Array _cachedIndexBuffer;
@@ -40,7 +31,7 @@ public:
SkinnedMesh(const SkinnedMesh& other)
: SkinnedMesh()
{
-#if !!BUILD_RELEASE
+#if !BUILD_RELEASE
CRASH; // Not used
#endif
}
@@ -58,7 +49,7 @@ public:
/// The skinned model
FORCE_INLINE SkinnedModel* GetSkinnedModel() const
{
- return _model;
+ return (SkinnedModel*)_model;
}
///
@@ -70,39 +61,6 @@ public:
return _index;
}
- ///
- /// Gets the material slot index.
- ///
- /// The material slot index
- API_PROPERTY() FORCE_INLINE int32 GetMaterialSlotIndex() const
- {
- return _materialSlotIndex;
- }
-
- ///
- /// Sets the index of the material slot index.
- ///
- /// The value.
- API_PROPERTY() void SetMaterialSlotIndex(int32 value);
-
- ///
- /// Gets the triangle count.
- ///
- /// The triangles
- API_PROPERTY() FORCE_INLINE int32 GetTriangleCount() const
- {
- return _triangles;
- }
-
- ///
- /// Gets the vertex count.
- ///
- /// The vertices
- API_PROPERTY() FORCE_INLINE int32 GetVertexCount() const
- {
- return _vertices;
- }
-
///
/// Determines whether this mesh is initialized (has vertex and index buffers initialized).
///
@@ -112,15 +70,6 @@ public:
return _vertexBuffer != nullptr;
}
- ///
- /// Determines whether this mesh is using 16 bit index buffer, otherwise it's 32 bit.
- ///
- /// True if this mesh is using 16 bit index buffer, otherwise 32 bit index buffer.
- API_PROPERTY() FORCE_INLINE bool Use16BitIndexBuffer() const
- {
- return _use16BitIndexBuffer;
- }
-
///
/// Blend shapes used by this mesh.
///
@@ -194,36 +143,6 @@ public:
/// True if failed, otherwise false.
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices);
-public:
-
- ///
- /// Sets the mesh bounds.
- ///
- /// The bounding box.
- void SetBounds(const BoundingBox& box)
- {
- _box = box;
- BoundingSphere::FromBox(box, _sphere);
- }
-
- ///
- /// Gets the box.
- ///
- /// The bounding box.
- API_PROPERTY() FORCE_INLINE BoundingBox GetBox() const
- {
- return _box;
- }
-
- ///
- /// Gets the sphere.
- ///
- /// The bounding sphere.
- API_PROPERTY() FORCE_INLINE BoundingSphere GetSphere() const
- {
- return _sphere;
- }
-
public:
///
@@ -319,29 +238,10 @@ public:
public:
- ///
- /// Extract mesh buffer data from the GPU (cannot be called from the main thread!).
- ///
- /// Buffer type
- /// The result data
- /// True if failed, otherwise false
- bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const;
-
- ///
- /// Extracts mesh buffer data from the GPU in the async task.
- ///
- /// Buffer type
- /// The result data
- /// Created async task used to gather the buffer data.
- Task* DownloadDataAsyncGPU(MeshBufferType type, BytesContainer& result) const;
-
- ///
- /// Extract mesh buffer data from the CPU.
- ///
- /// Buffer type
- /// The result data
- /// True if failed, otherwise false
- bool DownloadDataCPU(MeshBufferType type, BytesContainer& result) const;
+ // [MeshBase]
+ bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override;
+ Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const override;
+ bool DownloadDataCPU(MeshBufferType type, BytesContainer& result) const override;
private:
diff --git a/Source/Engine/Graphics/Models/SkinnedModelLOD.cpp b/Source/Engine/Graphics/Models/SkinnedModelLOD.cpp
index db0e8b86e..e2a43d085 100644
--- a/Source/Engine/Graphics/Models/SkinnedModelLOD.cpp
+++ b/Source/Engine/Graphics/Models/SkinnedModelLOD.cpp
@@ -5,6 +5,12 @@
#include "Engine/Content/Assets/Model.h"
#include "Engine/Serialization/MemoryReadStream.h"
+bool SkinnedModelLOD::HasAnyMeshInitialized() const
+{
+ // Note: we initialize all meshes at once so the last one can be used to check it.
+ return Meshes.HasItems() && Meshes.Last().IsInitialized();
+}
+
bool SkinnedModelLOD::Load(MemoryReadStream& stream)
{
// Load LOD for each mesh
diff --git a/Source/Engine/Graphics/Models/SkinnedModelLOD.h b/Source/Engine/Graphics/Models/SkinnedModelLOD.h
index 673bbb4e2..758db0a1b 100644
--- a/Source/Engine/Graphics/Models/SkinnedModelLOD.h
+++ b/Source/Engine/Graphics/Models/SkinnedModelLOD.h
@@ -33,12 +33,7 @@ public:
///
/// Determines whether any mesh has been initialized.
///
- /// True if any mesh has been initialized, otherwise false.
- FORCE_INLINE bool HasAnyMeshInitialized() const
- {
- // Note: we initialize all meshes at once so the last one can be used to check it.
- return Meshes.HasItems() && Meshes.Last().IsInitialized();
- }
+ bool HasAnyMeshInitialized() const;
public:
diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h
index 400deb3c1..e1607906f 100644
--- a/Source/Engine/Graphics/PostProcessSettings.h
+++ b/Source/Engine/Graphics/PostProcessSettings.h
@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Math/Vector3.h"
+#include "Engine/Core/Math/Vector4.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Serialization/ISerializable.h"
#include "Engine/Content/Assets/Texture.h"
diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h
index 5d6a47861..96048bcc2 100644
--- a/Source/Engine/Graphics/RenderTask.h
+++ b/Source/Engine/Graphics/RenderTask.h
@@ -251,7 +251,7 @@ public:
///
/// The scene rendering camera. Can be used to override the rendering view properties based on the current camera setup.
///
- API_FIELD() Camera* Camera = nullptr;
+ API_FIELD() ScriptingObjectReference Camera;
///
/// The render view description.
diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp
index 4fc5e2e1d..142906192 100644
--- a/Source/Engine/Graphics/RenderTools.cpp
+++ b/Source/Engine/Graphics/RenderTools.cpp
@@ -6,6 +6,7 @@
#include "PixelFormat.h"
#include "RenderView.h"
#include "GPUDevice.h"
+#include "RenderTask.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Engine/Time.h"
@@ -553,3 +554,20 @@ float ViewToCenterLessRadius(const RenderView& view, const Vector3& center, floa
// Calculate result
return viewToCenter - radius;
}
+
+void MeshBase::SetMaterialSlotIndex(int32 value)
+{
+ if (value < 0 || value >= _model->MaterialSlots.Count())
+ {
+ LOG(Warning, "Cannot set mesh material slot to {0} while model has {1} slots.", value, _model->MaterialSlots.Count());
+ return;
+ }
+
+ _materialSlotIndex = value;
+}
+
+void MeshBase::SetBounds(const BoundingBox& box)
+{
+ _box = box;
+ BoundingSphere::FromBox(box, _sphere);
+}
diff --git a/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp b/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp
index 4d8b8cf1d..8ce11ec70 100644
--- a/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp
+++ b/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp
@@ -4,6 +4,8 @@
#include "ShaderStorage.h"
#include "ShaderCacheManager.h"
#include "Engine/Engine/CommandLine.h"
+#include "Engine/Graphics/GPUDevice.h"
+#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/ShadowsOfMordor/AtlasChartsPacker.h"
@@ -31,6 +33,16 @@ ShaderStorage::CachingMode ShaderStorage::GetCachingMode()
#endif
+bool ShaderAssetBase::IsNullRenderer()
+{
+ return GPUDevice::Instance->GetRendererType() == RendererType::Null;
+}
+
+int32 ShaderAssetBase::GetCacheChunkIndex()
+{
+ return GetCacheChunkIndex(GPUDevice::Instance->GetShaderProfile());
+}
+
int32 ShaderAssetBase::GetCacheChunkIndex(ShaderProfile profile)
{
int32 result;
@@ -61,6 +73,28 @@ int32 ShaderAssetBase::GetCacheChunkIndex(ShaderProfile profile)
return result;
}
+bool ShaderAssetBase::initBase(AssetInitData& initData)
+{
+ // Validate version
+ if (initData.SerializedVersion != ShaderStorage::Header::Version)
+ {
+ LOG(Warning, "Invalid shader serialized version.");
+ return true;
+ }
+
+ // Validate data
+ if (initData.CustomData.Length() != sizeof(_shaderHeader))
+ {
+ LOG(Warning, "Invalid shader header.");
+ return true;
+ }
+
+ // Load header 'as-is'
+ Platform::MemoryCopy(&_shaderHeader, initData.CustomData.Get(), sizeof(_shaderHeader));
+
+ return false;
+}
+
#if USE_EDITOR
bool ShaderAssetBase::Save()
diff --git a/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.h b/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.h
index ab1f79aee..57262eed8 100644
--- a/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.h
+++ b/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.h
@@ -4,7 +4,6 @@
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Content/BinaryAsset.h"
-#include "Engine/Graphics/GPUDevice.h"
#include "ShaderStorage.h"
///
@@ -18,17 +17,15 @@ protected:
public:
- ///
- /// Gets internal shader cache chunk index
- ///
- /// Chunk index
- FORCE_INLINE static int32 GetCacheChunkIndex()
- {
- return GetCacheChunkIndex(GPUDevice::Instance->GetShaderProfile());
- }
+ static bool IsNullRenderer();
///
- /// Gets internal shader cache chunk index
+ /// Gets internal shader cache chunk index (for current GPU device shader profile).
+ ///
+ static int32 GetCacheChunkIndex();
+
+ ///
+ /// Gets internal shader cache chunk index.
///
/// Shader profile
/// Chunk index
@@ -47,11 +44,12 @@ public:
#endif
protected:
-
+
+ bool initBase(AssetInitData& initData);
+
///
/// Gets the parent asset.
///
- /// The asset.
virtual BinaryAsset* GetShaderAsset() const = 0;
#if USE_EDITOR
@@ -110,7 +108,7 @@ protected:
};
///
-/// Base class for assets that can contain shader
+/// Base class for binary assets that can contain shader.
///
template
class ShaderAssetTypeBase : public BaseType, public ShaderAssetBase
@@ -121,11 +119,6 @@ public:
protected:
- ///
- /// Init
- ///
- /// Asset scripting class metadata
- /// Asset information
explicit ShaderAssetTypeBase(const ScriptingObjectSpawnParams& params, const AssetInfo* info)
: BaseType(params, info)
{
@@ -134,38 +127,22 @@ protected:
protected:
// [BaseType]
- BinaryAsset* GetShaderAsset() const override
- {
- return (BinaryAsset*)this;
- }
bool init(AssetInitData& initData) override
{
- // Validate version
- if (initData.SerializedVersion != ShadersSerializedVersion)
- {
- LOG(Warning, "Invalid shader serialized version.");
- return true;
- }
-
- // Validate data
- if (initData.CustomData.Length() != sizeof(_shaderHeader))
- {
- LOG(Warning, "Invalid shader header.");
- return true;
- }
-
- // Load header 'as-is'
- Platform::MemoryCopy(&_shaderHeader, initData.CustomData.Get(), sizeof(_shaderHeader));
-
- return false;
+ return initBase(initData);
}
-
AssetChunksFlag getChunksToPreload() const override
{
AssetChunksFlag result = 0;
const auto cachingMode = ShaderStorage::GetCachingMode();
- if (cachingMode == ShaderStorage::CachingMode::AssetInternal && GPUDevice::Instance->GetRendererType() != RendererType::Null)
+ if (cachingMode == ShaderStorage::CachingMode::AssetInternal && IsNullRenderer())
result |= GET_CHUNK_FLAG(GetCacheChunkIndex());
return result;
}
+
+ // [ShaderAssetBase]
+ BinaryAsset* GetShaderAsset() const override
+ {
+ return (BinaryAsset*)this;
+ }
};
diff --git a/Source/Engine/Graphics/Textures/ITextureOwner.h b/Source/Engine/Graphics/Textures/ITextureOwner.h
index 8f66298ba..196b76664 100644
--- a/Source/Engine/Graphics/Textures/ITextureOwner.h
+++ b/Source/Engine/Graphics/Textures/ITextureOwner.h
@@ -18,8 +18,7 @@ public:
///
/// Gets the texture owner mutex used to synchronize texture logic.
///
- /// The mutex.
- virtual CriticalSection* GetOwnerLocker() const = 0;
+ virtual CriticalSection& GetOwnerLocker() const = 0;
///
/// Get texture mip map data
diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp
index 15b9c540c..d5a1e9339 100644
--- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp
+++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp
@@ -33,6 +33,23 @@ StreamingTexture::~StreamingTexture()
ASSERT(_streamingTasksCount == 0);
}
+Vector2 StreamingTexture::Size() const
+{
+ return _texture->Size();
+}
+
+int32 StreamingTexture::TextureMipIndexToTotalIndex(int32 textureMipIndex) const
+{
+ const int32 missingMips = TotalMipLevels() - _texture->MipLevels();
+ return textureMipIndex + missingMips;
+}
+
+int32 StreamingTexture::TotalIndexToTextureMipIndex(int32 mipIndex) const
+{
+ const int32 missingMips = TotalMipLevels() - _texture->MipLevels();
+ return mipIndex - missingMips;
+}
+
bool StreamingTexture::Create(const TextureHeader& header)
{
// Validate header (further validation is performed by the Texture.Init)
@@ -90,13 +107,27 @@ uint64 StreamingTexture::GetTotalMemoryUsage() const
return CalculateTextureMemoryUsage(_header.Format, _header.Width, _header.Height, _header.MipLevels) * arraySize;
}
+String StreamingTexture::ToString() const
+{
+ return _texture->ToString();
+}
+
+int32 StreamingTexture::GetCurrentResidency() const
+{
+ return _texture->ResidentMipLevels();
+}
+
+int32 StreamingTexture::GetAllocatedResidency() const
+{
+ return _texture->MipLevels();
+}
+
bool StreamingTexture::CanBeUpdated() const
{
// Streaming Texture cannot be updated if:
// - is not initialized
// - mip data uploading job running
// - resize texture job running
-
return IsInitialized() && Platform::AtomicRead(&_streamingTasksCount) == 0;
}
@@ -146,7 +177,6 @@ protected:
return Result::Ok;
}
-
void OnEnd() override
{
Platform::InterlockedDecrement(&_streamingTexture->_streamingTasksCount);
@@ -154,7 +184,6 @@ protected:
// Base
GPUTask::OnEnd();
}
-
void OnSync() override
{
Swap(_streamingTexture->_texture, _newTexture);
@@ -298,7 +327,6 @@ protected:
return Result::Ok;
}
-
void OnEnd() override
{
_dataLock.Release();
diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.h b/Source/Engine/Graphics/Textures/StreamingTexture.h
index 3b6e94959..1dda71ba4 100644
--- a/Source/Engine/Graphics/Textures/StreamingTexture.h
+++ b/Source/Engine/Graphics/Textures/StreamingTexture.h
@@ -2,7 +2,6 @@
#pragma once
-#include "GPUTexture.h"
#include "ITextureOwner.h"
#include "Engine/Streaming/StreamableResource.h"
#include "Types.h"
@@ -14,7 +13,6 @@ class FLAXENGINE_API StreamingTexture : public Object, public StreamableResource
{
friend class StreamTextureMipTask;
friend class StreamTextureResizeTask;
-
protected:
ITextureOwner* _owner;
@@ -25,16 +23,7 @@ protected:
public:
- ///
- /// Init
- ///
- /// Parent object
- /// Texture object name
StreamingTexture(ITextureOwner* owner, const String& name);
-
- ///
- /// Destructor
- ///
~StreamingTexture();
public:
@@ -42,31 +31,23 @@ public:
///
/// Gets the owner.
///
- /// The owner.
FORCE_INLINE ITextureOwner* GetOwner() const
{
return _owner;
}
///
- /// Gets texture object handle
+ /// Gets texture object handle.
///
- /// Texture object
FORCE_INLINE GPUTexture* GetTexture() const
{
return _texture;
}
///
- /// Gets texture size of Vector2::Zero if not loaded
+ /// Gets texture size of Vector2::Zero if not loaded.
///
- /// Texture size
- FORCE_INLINE Vector2 Size() const
- {
- return _texture->Size();
- }
-
-public:
+ Vector2 Size() const;
///
/// Gets a value indicating whether this instance is initialized.
@@ -88,7 +69,6 @@ public:
///
/// Gets total texture height (in texels)
///
- /// Texture width
FORCE_INLINE int32 TotalHeight() const
{
return _header.Height;
@@ -97,7 +77,6 @@ public:
///
/// Gets total texture array size
///
- /// Texture array size
FORCE_INLINE int32 TotalArraySize() const
{
return IsCubeMap() ? 6 : 1;
@@ -106,7 +85,6 @@ public:
///
/// Gets total texture mip levels count
///
- /// Texture mip levels
FORCE_INLINE int32 TotalMipLevels() const
{
return _header.MipLevels;
@@ -115,7 +93,6 @@ public:
///
/// Returns texture format type
///
- /// Texture format type
FORCE_INLINE TextureFormatType GetFormatType() const
{
return _header.Type;
@@ -124,7 +101,6 @@ public:
///
/// Returns true if it's a cube map texture
///
- /// True if i's a cubemap
FORCE_INLINE bool IsCubeMap() const
{
return _header.IsCubeMap;
@@ -133,7 +109,6 @@ public:
///
/// Returns true if texture cannot be used during GPU resources streaming system
///
- /// True if texture cannot be used during GPU resources streaming system
FORCE_INLINE bool NeverStream() const
{
return _header.NeverStream;
@@ -142,7 +117,6 @@ public:
///
/// Gets the texture header.
///
- /// Header
FORCE_INLINE const TextureHeader* GetHeader() const
{
return &_header;
@@ -163,22 +137,14 @@ public:
///
/// Index of the texture mip.
/// The index of the mip map.
- FORCE_INLINE int32 TextureMipIndexToTotalIndex(int32 textureMipIndex) const
- {
- const int32 missingMips = TotalMipLevels() - _texture->MipLevels();
- return textureMipIndex + missingMips;
- }
+ int32 TextureMipIndexToTotalIndex(int32 textureMipIndex) const;
///
/// Converts absolute mip map index to the allocated texture mip index.
///
/// Index of the texture mip.
/// The index of the mip map.
- FORCE_INLINE int32 TotalIndexToTextureMipIndex(int32 mipIndex) const
- {
- const int32 missingMips = TotalMipLevels() - _texture->MipLevels();
- return mipIndex - missingMips;
- }
+ int32 TotalIndexToTextureMipIndex(int32 mipIndex) const;
public:
@@ -211,27 +177,15 @@ public:
public:
// [Object]
- String ToString() const override
- {
- return _texture->ToString();
- }
+ String ToString() const override;
// [StreamableResource]
int32 GetMaxResidency() const override
{
return _header.MipLevels;
}
-
- int32 GetCurrentResidency() const override
- {
- return _texture->ResidentMipLevels();
- }
-
- int32 GetAllocatedResidency() const override
- {
- return _texture->MipLevels();
- }
-
+ int32 GetCurrentResidency() const override;
+ int32 GetAllocatedResidency() const override;
bool CanBeUpdated() const override;
Task* UpdateAllocation(int32 residency) override;
Task* CreateStreamingTask(int32 residency) override;
diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp
index 83e781a5a..d21d61f2f 100644
--- a/Source/Engine/Graphics/Textures/TextureBase.cpp
+++ b/Source/Engine/Graphics/Textures/TextureBase.cpp
@@ -3,10 +3,11 @@
#include "TextureBase.h"
#include "TextureData.h"
#include "Engine/Graphics/GPUDevice.h"
-#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
-#include "Engine/Debug/Exceptions/InvalidOperationException.h"
+#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
+#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
+#include "Engine/Debug/Exceptions/InvalidOperationException.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
@@ -22,6 +23,36 @@ TextureBase::TextureBase(const SpawnParams& params, const AssetInfo* info)
{
}
+Vector2 TextureBase::Size() const
+{
+ return Vector2(static_cast(_texture.TotalWidth()), static_cast(_texture.TotalHeight()));
+}
+
+int32 TextureBase::GetArraySize() const
+{
+ return StreamingTexture()->TotalArraySize();
+}
+
+int32 TextureBase::GetMipLevels() const
+{
+ return StreamingTexture()->TotalMipLevels();
+}
+
+int32 TextureBase::GetResidentMipLevels() const
+{
+ return GetTexture()->ResidentMipLevels();
+}
+
+uint64 TextureBase::GetCurrentMemoryUsage() const
+{
+ return GetTexture()->GetMemoryUsage();
+}
+
+uint64 TextureBase::GetTotalMemoryUsage() const
+{
+ return StreamingTexture()->GetTotalMemoryUsage();
+}
+
BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& slicePitch)
{
BytesContainer result;
@@ -193,9 +224,9 @@ int32 TextureBase::calculateChunkIndex(int32 mipIndex) const
return mipIndex;
}
-CriticalSection* TextureBase::GetOwnerLocker() const
+CriticalSection& TextureBase::GetOwnerLocker() const
{
- return &_parent->Locker;
+ return _parent->Locker;
}
void TextureBase::unload(bool isReloading)
diff --git a/Source/Engine/Graphics/Textures/TextureBase.h b/Source/Engine/Graphics/Textures/TextureBase.h
index 2cb895a23..7839f940a 100644
--- a/Source/Engine/Graphics/Textures/TextureBase.h
+++ b/Source/Engine/Graphics/Textures/TextureBase.h
@@ -6,6 +6,8 @@
#include "StreamingTexture.h"
#include "Engine/Core/Log.h"
+class TextureData;
+
///
/// Base class for , , and other assets that can contain texture data.
///
@@ -103,50 +105,32 @@ public:
///
/// Gets the total size of the texture. Actual resident size may be different due to dynamic content streaming. Returns Vector2::Zero if texture is not loaded.
///
- API_PROPERTY() FORCE_INLINE Vector2 Size() const
- {
- return Vector2(static_cast(_texture.TotalWidth()), static_cast(_texture.TotalHeight()));
- }
+ API_PROPERTY() Vector2 Size() const;
///
/// Gets the total array size of the texture.
///
- API_PROPERTY() int32 GetArraySize() const
- {
- return StreamingTexture()->TotalArraySize();
- }
+ API_PROPERTY() int32 GetArraySize() const;
///
/// Gets the total mip levels count of the texture. Actual resident mipmaps count may be different due to dynamic content streaming.
///
- API_PROPERTY() int32 GetMipLevels() const
- {
- return StreamingTexture()->TotalMipLevels();
- }
+ API_PROPERTY() int32 GetMipLevels() const;
///
/// Gets the current mip levels count of the texture that are on GPU ready to use.
///
- API_PROPERTY() int32 GetResidentMipLevels() const
- {
- return GetTexture()->ResidentMipLevels();
- }
+ API_PROPERTY() int32 GetResidentMipLevels() const;
///
/// Gets the amount of the memory used by this resource. Exact value may differ due to memory alignment and resource allocation policy.
///
- API_PROPERTY() uint64 GetCurrentMemoryUsage() const
- {
- return GetTexture()->GetMemoryUsage();
- }
+ API_PROPERTY() uint64 GetCurrentMemoryUsage() const;
///
/// Gets the total memory usage that texture may have in use (if loaded to the maximum quality). Exact value may differ due to memory alignment and resource allocation policy.
///
- API_PROPERTY() uint64 GetTotalMemoryUsage() const
- {
- return StreamingTexture()->GetTotalMemoryUsage();
- }
+ API_PROPERTY() uint64 GetTotalMemoryUsage() const;
public:
@@ -186,7 +170,7 @@ private:
public:
// [ITextureOwner]
- CriticalSection* GetOwnerLocker() const override;
+ CriticalSection& GetOwnerLocker() const override;
Task* RequestMipDataAsync(int32 mipIndex) override;
FlaxStorage::LockData LockData() override;
void GetMipData(int32 mipIndex, BytesContainer& data) const override;
diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp
index f9a39574c..ac56a2a9f 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp
@@ -43,7 +43,7 @@ void CmdBufferVulkan::End()
{
ASSERT(IsOutsideRenderPass());
-#if GPU_ALLOW_PROFILE_EVENTS
+#if GPU_ALLOW_PROFILE_EVENTS && VK_EXT_debug_utils
// End remaining events
while (_eventsBegin--)
vkCmdEndDebugUtilsLabelEXT(GetHandle());
@@ -89,6 +89,7 @@ void CmdBufferVulkan::AcquirePoolSet()
void CmdBufferVulkan::BeginEvent(const Char* name)
{
+#if VK_EXT_debug_utils
if (!vkCmdBeginDebugUtilsLabelEXT)
return;
@@ -108,15 +109,18 @@ void CmdBufferVulkan::BeginEvent(const Char* name)
RenderToolsVulkan::ZeroStruct(label, VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT);
label.pLabelName = buffer;
vkCmdBeginDebugUtilsLabelEXT(GetHandle(), &label);
+#endif
}
void CmdBufferVulkan::EndEvent()
{
+#if VK_EXT_debug_utils
if (_eventsBegin == 0 || !vkCmdEndDebugUtilsLabelEXT)
return;
_eventsBegin--;
vkCmdEndDebugUtilsLabelEXT(GetHandle());
+#endif
}
#endif
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.cpp
index 1cd90b11b..2acd2712c 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.cpp
@@ -17,14 +17,17 @@ void GPUBufferViewVulkan::Init(GPUDeviceVulkan* device, GPUBufferVulkan* owner,
Buffer = buffer;
Size = size;
- VkBufferViewCreateInfo viewInfo;
- RenderToolsVulkan::ZeroStruct(viewInfo, VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO);
- viewInfo.buffer = Buffer;
- viewInfo.format = RenderToolsVulkan::ToVulkanFormat(format);
- viewInfo.offset = 0;
- viewInfo.range = Size;
-
- VALIDATE_VULKAN_RESULT(vkCreateBufferView(device->Device, &viewInfo, nullptr, &View));
+ if (owner->IsShaderResource() && !(owner->GetDescription().Flags & GPUBufferFlags::Structured))
+ {
+ VkBufferViewCreateInfo viewInfo;
+ RenderToolsVulkan::ZeroStruct(viewInfo, VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO);
+ viewInfo.buffer = Buffer;
+ viewInfo.format = RenderToolsVulkan::ToVulkanFormat(format);
+ viewInfo.offset = 0;
+ viewInfo.range = Size;
+ ASSERT_LOW_LAYER(viewInfo.format != VK_FORMAT_UNDEFINED);
+ VALIDATE_VULKAN_RESULT(vkCreateBufferView(device->Device, &viewInfo, nullptr, &View));
+ }
}
void GPUBufferViewVulkan::Release()
@@ -32,19 +35,18 @@ void GPUBufferViewVulkan::Release()
if (View != VK_NULL_HANDLE)
{
Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::BufferView, View);
-
View = VK_NULL_HANDLE;
-
-#if BUILD_DEBUG
- Device = nullptr;
- Owner = nullptr;
- Buffer = VK_NULL_HANDLE;
-#endif
}
+#if BUILD_DEBUG
+ Device = nullptr;
+ Owner = nullptr;
+ Buffer = VK_NULL_HANDLE;
+#endif
}
void GPUBufferViewVulkan::DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
{
+ ASSERT_LOW_LAYER(View != VK_NULL_HANDLE);
bufferView = &View;
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
@@ -52,6 +54,7 @@ void GPUBufferViewVulkan::DescriptorAsUniformTexelBuffer(GPUContextVulkan* conte
void GPUBufferViewVulkan::DescriptorAsStorageBuffer(GPUContextVulkan* context, VkBuffer& buffer, VkDeviceSize& offset, VkDeviceSize& range)
{
+ ASSERT_LOW_LAYER(Buffer);
buffer = Buffer;
offset = 0;
range = Size;
@@ -88,7 +91,7 @@ bool GPUBufferVulkan::OnInit()
bufferInfo.size = _desc.Size;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- if (useSRV)
+ if (useSRV && !(_desc.Flags & GPUBufferFlags::Structured))
bufferInfo.usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
if (useUAV || _desc.Flags & GPUBufferFlags::RawBuffer)
bufferInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
@@ -165,7 +168,7 @@ bool GPUBufferVulkan::OnInit()
}
}
// Check if need to bind buffer to the shaders
- else if (useSRV)
+ else if (useSRV || useUAV)
{
// Create buffer view
_view.Init(_device, this, _buffer, _desc.Size, _desc.Format);
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
index aa169e8f5..a0b64a3bb 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp
@@ -27,6 +27,7 @@
#include "Engine/Core/Utilities.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Engine/Engine.h"
+#include "Engine/Engine/Globals.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Profiler/ProfilerCPU.h"
@@ -52,6 +53,8 @@ VkDebugReportCallbackEXT MsgCallback = VK_NULL_HANDLE;
extern VulkanValidationLevel ValidationLevel;
+#if VK_EXT_debug_report
+
static VKAPI_ATTR VkBool32 VKAPI_PTR DebugReportFunction(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32 msgCode, const char* layerPrefix, const char* msg, void* userData)
{
const Char* msgPrefix = TEXT("UNKNOWN");
@@ -126,6 +129,8 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugReportFunction(VkDebugReportFlagsEXT m
return VK_FALSE;
}
+#endif
+
#if VK_EXT_debug_utils
static VKAPI_ATTR VkBool32 VKAPI_PTR DebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT msgSeverity, VkDebugUtilsMessageTypeFlagsEXT msgType, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData)
@@ -140,8 +145,8 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugUtilsCallback(VkDebugUtilsMessageSever
case 2: // Fragment shader writes to output location 0 with no matching attachment
case 3: // Attachment 2 not written by fragment shader
case 5: // SPIR-V module not valid: MemoryBarrier: Vulkan specification requires Memory Semantics to have one of the following bits set: Acquire, Release, AcquireRelease or SequentiallyConsistent
-#if PLATFORM_ANDROID
case -1666394502: // After query pool creation, each query must be reset before it is used. Queries must also be reset between uses.
+#if PLATFORM_ANDROID
case 602160055: // Attachment 4 not written by fragment shader; undefined values will be written to attachment. TODO: investigate it for PS_GBuffer shader from Deferred material with USE_LIGHTMAP=1
#endif
return VK_FALSE;
@@ -270,6 +275,7 @@ void SetupDebugLayerCallback()
if (SupportsDebugCallbackExt)
#endif
{
+#if VK_EXT_debug_report
if (vkCreateDebugReportCallbackEXT)
{
VkDebugReportCallbackCreateInfoEXT createInfo;
@@ -303,6 +309,7 @@ void SetupDebugLayerCallback()
{
LOG(Warning, "GetProcAddr: Unable to find vkDbgCreateMsgCallback; debug reporting skipped!");
}
+#endif
}
else
{
@@ -324,8 +331,10 @@ void RemoveDebugLayerCallback()
if (MsgCallback != VK_NULL_HANDLE)
#endif
{
+#if VK_EXT_debug_report
if (vkDestroyDebugReportCallbackEXT)
vkDestroyDebugReportCallbackEXT(GPUDeviceVulkan::Instance, MsgCallback, nullptr);
+#endif
MsgCallback = VK_NULL_HANDLE;
}
}
@@ -344,7 +353,7 @@ DeferredDeletionQueueVulkan::~DeferredDeletionQueueVulkan()
void DeferredDeletionQueueVulkan::ReleaseResources(bool deleteImmediately)
{
- ScopeLock lock(&_locker);
+ ScopeLock lock(_locker);
const uint64 checkFrame = Engine::FrameCount - VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT;
for (int32 i = 0; i < _entries.Count(); i++)
{
@@ -1032,7 +1041,7 @@ void StagingManagerVulkan::Dispose()
ScopeLock lock(_locker);
#if !BUILD_RELEASE
- LOG(Info, "Vulakn staging buffers peek memory usage: {0}, allocs: {1}, frees: {2}", Utilities::BytesToText(_allBuffersPeekSize), Utilities::BytesToText(_allBuffersAllocSize), Utilities::BytesToText(_allBuffersFreeSize));
+ LOG(Info, "Vulkan staging buffers peek memory usage: {0}, allocs: {1}, frees: {2}", Utilities::BytesToText(_allBuffersPeekSize), Utilities::BytesToText(_allBuffersAllocSize), Utilities::BytesToText(_allBuffersFreeSize));
#endif
// Release buffers and clear memory
@@ -1085,13 +1094,17 @@ GPUDevice* GPUDeviceVulkan::Create()
}
#endif
+ VkResult result;
+
+#if !PLATFORM_SWITCH
// Initialize bindings
- VkResult result = volkInitialize();
+ result = volkInitialize();
if (result != VK_SUCCESS)
{
LOG(Warning, "Graphics Device init failed with error {0}", RenderToolsVulkan::GetVkErrorString(result));
return nullptr;
}
+#endif
// Engine registration
const StringAsANSI<256> appName(*Globals::ProductName);
@@ -1170,10 +1183,12 @@ GPUDevice* GPUDeviceVulkan::Create()
return nullptr;
}
+#if !PLATFORM_SWITCH
// Setup bindings
volkLoadInstance(Instance);
+#endif
- // Setup debug layer
+// Setup debug layer
#if VULKAN_USE_DEBUG_LAYER
SetupDebugLayerCallback();
#endif
@@ -1658,8 +1673,10 @@ bool GPUDeviceVulkan::Init()
// Create the device
VALIDATE_VULKAN_RESULT(vkCreateDevice(gpu, &deviceInfo, nullptr, &Device));
+#if !PLATFORM_SWITCH
// Optimize bindings
volkLoadDevice(Device);
+#endif
// Create queues
if (graphicsQueueFamilyIndex == -1)
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs b/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs
index 68eb5ffcc..4779beb4f 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs
+++ b/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs
@@ -108,7 +108,15 @@ public class GraphicsDeviceVulkan : GraphicsDeviceBaseModule
options.PublicDefinitions.Add("GRAPHICS_API_VULKAN");
- options.PrivateDependencies.Add("volk");
options.PrivateDependencies.Add("VulkanMemoryAllocator");
+
+ if (options.Platform.Target == TargetPlatform.Switch)
+ {
+ options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "GraphicsDevice", "Vulkan"));
+ }
+ else
+ {
+ options.PrivateDependencies.Add("volk");
+ }
}
}
diff --git a/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h b/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h
index 3e00a9e87..fec251eca 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/IncludeVulkanHeaders.h
@@ -8,6 +8,19 @@
#include "Engine/Core/Math/Math.h"
+#if PLATFORM_SWITCH
+
+#define VK_USE_PLATFORM_VI_NN 1
+#include
+#undef VK_EXT_debug_utils
+#undef VK_EXT_debug_report
+#undef VK_EXT_validation_cache
+#define VMA_DEDICATED_ALLOCATION 0
+#pragma clang diagnostic ignored "-Wpointer-bool-conversion"
+#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
+
+#else
+
#if PLATFORM_WIN32
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
#endif
@@ -17,6 +30,8 @@
// License: MIT
#include
+#endif
+
// Use Vulkan Memory Allocator for buffer and image memory allocations
// Source: https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/
// License: MIT
diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp
index e974396a4..d24c5742d 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.cpp
@@ -241,7 +241,9 @@ String RenderToolsVulkan::GetVkErrorString(VkResult result)
VKERR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
VKERR(VK_ERROR_VALIDATION_FAILED_EXT);
VKERR(VK_ERROR_INVALID_SHADER_NV);
+#if VK_HEADER_VERSION >= 89
VKERR(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);
+#endif
VKERR(VK_ERROR_FRAGMENTATION_EXT);
VKERR(VK_ERROR_NOT_PERMITTED_EXT);
#if VK_HEADER_VERSION < 140
diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h
index cf9b7e9cf..ec5d882bc 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h
@@ -78,6 +78,9 @@ public:
case 0:
stageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
break;
+ case VK_ACCESS_INDIRECT_COMMAND_READ_BIT:
+ stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ break;
case VK_ACCESS_TRANSFER_WRITE_BIT:
stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
diff --git a/Source/Engine/GraphicsDevice/Vulkan/VulkanPlatform.h b/Source/Engine/GraphicsDevice/Vulkan/VulkanPlatform.h
index 8e585c78e..bc216190b 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/VulkanPlatform.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/VulkanPlatform.h
@@ -8,4 +8,6 @@
#include "Linux/LinuxVulkanPlatform.h"
#elif PLATFORM_ANDROID
#include "Android/AndroidVulkanPlatform.h"
+#elif PLATFORM_SWITCH
+#include "Platforms/Switch/Engine/GraphicsDevice/Vulkan/SwitchVulkanPlatform.h"
#endif
diff --git a/Source/Engine/Input/InputSettings.h b/Source/Engine/Input/InputSettings.h
index 7771028e8..fd594f3ee 100644
--- a/Source/Engine/Input/InputSettings.h
+++ b/Source/Engine/Input/InputSettings.h
@@ -5,6 +5,7 @@
#include "Engine/Core/Config/Settings.h"
#include "Engine/Serialization/JsonTools.h"
#include "VirtualInput.h"
+#include "Engine/Core/Collections/Array.h"
///
/// Input settings container.
diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp
index 8ab195ef1..33d107579 100644
--- a/Source/Engine/Level/Actor.cpp
+++ b/Source/Engine/Level/Actor.cpp
@@ -15,7 +15,9 @@
#include "Engine/Core/Cache.h"
#include "Engine/Core/Collections/CollectionPoolCache.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderView.h"
+#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Serialization/ISerializeModifier.h"
#include "Engine/Serialization/Serialization.h"
@@ -347,6 +349,12 @@ void Actor::SetOrderInParent(int32 index)
}
}
+Actor* Actor::GetChild(int32 index) const
+{
+ CHECK_RETURN(index >= 0 && index < Children.Count(), nullptr);
+ return Children[index];
+}
+
Actor* Actor::GetChild(const StringView& name) const
{
for (int32 i = 0; i < Children.Count(); i++)
@@ -354,7 +362,6 @@ Actor* Actor::GetChild(const StringView& name) const
if (Children[i]->GetName() == name)
return Children[i];
}
-
return nullptr;
}
@@ -482,6 +489,12 @@ void Actor::SetName(const StringView& value)
Level::callActorEvent(Level::ActorEventType::OnActorNameChanged, this, nullptr);
}
+Script* Actor::GetScript(int32 index) const
+{
+ CHECK_RETURN(index >= 0 && index < Scripts.Count(), nullptr);
+ return Scripts[index];
+}
+
Script* Actor::GetScript(const MClass* type) const
{
CHECK_RETURN(type, nullptr);
@@ -1483,6 +1496,7 @@ void WriteObjectToBytes(SceneObject* obj, rapidjson_flax::StringBuffer& buffer,
bool Actor::ToBytes(const Array& actors, MemoryWriteStream& output)
{
+ PROFILE_CPU();
if (actors.IsEmpty())
{
// Cannot serialize empty list
@@ -1542,6 +1556,7 @@ Array Actor::ToBytes(const Array& actors)
bool Actor::FromBytes(const Span& data, Array& output, ISerializeModifier* modifier)
{
+ PROFILE_CPU();
output.Clear();
ASSERT(modifier);
@@ -1586,7 +1601,7 @@ bool Actor::FromBytes(const Span& data, Array& output, ISerializeM
// Order in parent
int32 orderInParent;
stream.ReadInt32(&orderInParent);
- order.At(i) = orderInParent;
+ order[i] = orderInParent;
// Load JSON
rapidjson_flax::Document document;
@@ -1626,7 +1641,6 @@ bool Actor::FromBytes(const Span& data, Array& output, ISerializeM
// Order in parent
int32 orderInParent;
stream.ReadInt32(&orderInParent);
- order.Add(orderInParent);
// Load JSON
rapidjson_flax::Document document;
@@ -1644,17 +1658,19 @@ bool Actor::FromBytes(const Span& data, Array& output, ISerializeM
Scripting::ObjectsLookupIdMapping.Set(nullptr);
// Link objects
- for (int32 i = 0; i < objectsCount; i++)
+ //for (int32 i = 0; i < objectsCount; i++)
{
- SceneObject* obj = sceneObjects->At(i);
- obj->PostLoad();
+ //SceneObject* obj = sceneObjects->At(i);
+ // TODO: post load or post spawn?
+ //obj->PostLoad();
}
// Update objects order
- for (int32 i = 0; i < objectsCount; i++)
+ //for (int32 i = 0; i < objectsCount; i++)
{
- SceneObject* obj = sceneObjects->At(i);
- obj->SetOrderInParent(order[i]);
+ //SceneObject* obj = sceneObjects->At(i);
+ // TODO: remove order from saved data?
+ //obj->SetOrderInParent(order[i]);
}
// Call events (only for parents because they will propagate events down the tree)
@@ -1671,6 +1687,10 @@ bool Actor::FromBytes(const Span& data, Array& output, ISerializeM
}
}
for (int32 i = 0; i < parents->Count(); i++)
+ {
+ parents->At(i)->PostSpawn();
+ }
+ for (int32 i = 0; i < parents->Count(); i++)
{
Actor* actor = parents->At(i);
actor->OnTransformChanged();
@@ -1712,6 +1732,7 @@ Array Actor::FromBytes(const Span& data, const Dictionary Actor::TryGetSerializedObjectsIds(const Span& data)
{
+ PROFILE_CPU();
Array result;
if (data.Length() > 0)
{
@@ -1732,6 +1753,7 @@ Array Actor::TryGetSerializedObjectsIds(const Span& data)
String Actor::ToJson()
{
+ PROFILE_CPU();
rapidjson_flax::StringBuffer buffer;
CompactJsonWriter writer(buffer);
writer.SceneObject(this);
@@ -1743,6 +1765,8 @@ String Actor::ToJson()
void Actor::FromJson(const StringAnsiView& json)
{
+ PROFILE_CPU();
+
// Load JSON
rapidjson_flax::Document document;
document.Parse(json.Get(), json.Length());
diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h
index 233ebe04c..560403890 100644
--- a/Source/Engine/Level/Actor.h
+++ b/Source/Engine/Level/Actor.h
@@ -192,10 +192,7 @@ public:
///
/// The child actor index.
/// The child actor (always valid).
- API_FUNCTION() FORCE_INLINE Actor* GetChild(int32 index) const
- {
- return Children[index];
- }
+ API_FUNCTION() Actor* GetChild(int32 index) const;
///
/// Gets the child actor with the given name.
@@ -266,10 +263,7 @@ public:
///
/// The script index.
/// The script (always valid).
- API_FUNCTION() FORCE_INLINE Script* GetScript(int32 index) const
- {
- return Scripts[index];
- }
+ API_FUNCTION() Script* GetScript(int32 index) const;
///
/// Gets the script of the given type from this actor.
diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index f0ae7017e..842621e1d 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -8,6 +8,7 @@
#include "Editor/Editor.h"
#endif
#include "Engine/Graphics/GPUDevice.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/SceneObjectsFactory.h"
#include "Engine/Serialization/Serialization.h"
diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h
index d5d9b4424..bbced6878 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.h
+++ b/Source/Engine/Level/Actors/AnimatedModel.h
@@ -6,6 +6,7 @@
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Content/Assets/AnimationGraph.h"
#include "Engine/Graphics/Models/SkinnedMeshDrawData.h"
+#include "Engine/Renderer/DrawCall.h"
///
/// Performs an animation and renders a skinned model.
diff --git a/Source/Engine/Level/Actors/BoxBrush.cpp b/Source/Engine/Level/Actors/BoxBrush.cpp
index f02dd5bd3..4330424c7 100644
--- a/Source/Engine/Level/Actors/BoxBrush.cpp
+++ b/Source/Engine/Level/Actors/BoxBrush.cpp
@@ -128,6 +128,13 @@ void BoxBrush::GetSurfaces(CSG::Surface surfaces[6])
}
}
+void BoxBrush::SetMaterial(int32 surfaceIndex, MaterialBase* material)
+{
+ CHECK(Math::IsInRange(surfaceIndex, 0, 5));
+ Surfaces[surfaceIndex].Material = material;
+ OnBrushModified();
+}
+
bool BoxBrush::Intersects(int32 surfaceIndex, const Ray& ray, float& distance, Vector3& normal) const
{
distance = MAX_float;
@@ -232,6 +239,26 @@ void BoxBrush::OnDebugDrawSelected()
#endif
+Scene* BoxBrush::GetBrushScene() const
+{
+ return GetScene();
+}
+
+Guid BoxBrush::GetBrushID() const
+{
+ return GetID();
+}
+
+bool BoxBrush::CanUseCSG() const
+{
+ return IsActiveInHierarchy();
+}
+
+CSG::Mode BoxBrush::GetBrushMode() const
+{
+ return _mode;
+}
+
void BoxBrush::GetSurfaces(Array& surfaces)
{
surfaces.Clear();
@@ -240,6 +267,11 @@ void BoxBrush::GetSurfaces(Array& surfaces)
GetSurfaces(surfaces.Get());
}
+int32 BoxBrush::GetSurfacesCount()
+{
+ return 6;
+}
+
void BoxBrush::OnTransformChanged()
{
// Base
@@ -272,7 +304,7 @@ void BoxBrush::OnParentChanged()
{
// Base
Actor::OnParentChanged();
-
+
if (!IsDuringPlay())
return;
diff --git a/Source/Engine/Level/Actors/BoxBrush.h b/Source/Engine/Level/Actors/BoxBrush.h
index e8fc63e3f..ae9be5b83 100644
--- a/Source/Engine/Level/Actors/BoxBrush.h
+++ b/Source/Engine/Level/Actors/BoxBrush.h
@@ -153,6 +153,13 @@ public:
/// Surfaces
void GetSurfaces(CSG::Surface surfaces[6]);
+ ///
+ /// Sets the brush surface material.
+ ///
+ /// The brush surface index.
+ /// The material.
+ API_FUNCTION() void SetMaterial(int32 surfaceIndex, MaterialBase* material);
+
public:
///
@@ -169,7 +176,7 @@ public:
/// Otherwise performs simple vs test.
/// For more efficient collisions detection and ray casting use physics.
///
- /// The brush surface index..
+ /// The brush surface index.
/// The ray to test.
/// When the method completes and returns true, contains the distance of the intersection (if any valid).
/// When the method completes, contains the intersection surface normal vector (if any valid).
@@ -179,7 +186,7 @@ public:
///
/// Gets the brush surface triangles array (group by 3 vertices).
///
- /// The brush surface index..
+ /// The brush surface index.
/// The output vertices buffer with triangles or empty if no data loaded.
API_FUNCTION() void GetVertices(int32 surfaceIndex, API_PARAM(Out) Array& outputData) const;
@@ -204,32 +211,12 @@ public:
#endif
// [CSG::Brush]
- Scene* GetBrushScene() const override
- {
- return GetScene();
- }
-
- Guid GetBrushID() const override
- {
- return GetID();
- }
-
- bool CanUseCSG() const override
- {
- return IsActiveInHierarchy();
- }
-
- CSG::Mode GetBrushMode() const override
- {
- return _mode;
- }
-
+ Scene* GetBrushScene() const override;
+ Guid GetBrushID() const override;
+ bool CanUseCSG() const override;
+ CSG::Mode GetBrushMode() const override;
void GetSurfaces(Array& surfaces) override;
-
- int32 GetSurfacesCount() override
- {
- return 6;
- }
+ int32 GetSurfacesCount() override;
protected:
diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp
index 1f9de6d41..1cc7ba53b 100644
--- a/Source/Engine/Level/Actors/Camera.cpp
+++ b/Source/Engine/Level/Actors/Camera.cpp
@@ -5,24 +5,27 @@
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Content.h"
-#include "Engine/Platform/Window.h"
#include "Engine/Serialization/Serialization.h"
-#include "Engine/Level/Scene/SceneRendering.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Editor/Managed/ManagedEditor.h"
+#include "Engine/Renderer/DrawCall.h"
+#include "Engine/Graphics/RenderTask.h"
+#include "Engine/Level/Scene/SceneRendering.h"
#else
#include "Engine/Engine/Engine.h"
+#include "Engine/Platform/Window.h"
#endif
Array Camera::Cameras;
Camera* Camera::CutSceneCamera = nullptr;
-Camera* Camera::OverrideMainCamera = nullptr;
+ScriptingObjectReference Camera::OverrideMainCamera;
Camera* Camera::GetMainCamera()
{
- if (OverrideMainCamera)
- return OverrideMainCamera;
+ Camera* overrideMainCamera = OverrideMainCamera.Get();
+ if (overrideMainCamera)
+ return overrideMainCamera;
if (CutSceneCamera)
return CutSceneCamera;
return Cameras.HasItems() ? Cameras.First() : nullptr;
diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h
index 4bf277279..505479e98 100644
--- a/Source/Engine/Level/Actors/Camera.h
+++ b/Source/Engine/Level/Actors/Camera.h
@@ -8,6 +8,7 @@
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Core/Math/Ray.h"
#include "Engine/Core/Types/LayersMask.h"
+#include "Engine/Scripting/ScriptingObjectReference.h"
#if USE_EDITOR
#include "Engine/Content/AssetReference.h"
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
@@ -28,7 +29,7 @@ DECLARE_SCENE_OBJECT(Camera);
static Camera* CutSceneCamera;
// The overriden main camera.
- API_FIELD() static Camera* OverrideMainCamera;
+ API_FIELD() static ScriptingObjectReference OverrideMainCamera;
// Gets the main camera.
API_PROPERTY() static Camera* GetMainCamera();
diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.cpp b/Source/Engine/Level/Actors/EnvironmentProbe.cpp
index 9a2bb08a8..d5939d722 100644
--- a/Source/Engine/Level/Actors/EnvironmentProbe.cpp
+++ b/Source/Engine/Level/Actors/EnvironmentProbe.cpp
@@ -3,6 +3,7 @@
#include "EnvironmentProbe.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Graphics/RenderView.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/ProbesRenderer.h"
diff --git a/Source/Engine/Level/Actors/Light.cpp b/Source/Engine/Level/Actors/Light.cpp
index 56e3368b0..9f80216c5 100644
--- a/Source/Engine/Level/Actors/Light.cpp
+++ b/Source/Engine/Level/Actors/Light.cpp
@@ -2,6 +2,9 @@
#include "Light.h"
#include "../Scene/Scene.h"
+#if USE_EDITOR
+#include "Engine/Graphics/RenderView.h"
+#endif
#include "Engine/Serialization/Serialization.h"
Light::Light(const SpawnParams& params)
diff --git a/Source/Engine/Level/Actors/Sky.cpp b/Source/Engine/Level/Actors/Sky.cpp
index 49f46e9b9..1e35c27f0 100644
--- a/Source/Engine/Level/Actors/Sky.cpp
+++ b/Source/Engine/Level/Actors/Sky.cpp
@@ -4,14 +4,17 @@
#include "DirectionalLight.h"
#include "Engine/Core/Math/Color.h"
#include "Engine/Content/Content.h"
-#include "Engine/Graphics/RenderView.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/AtmospherePreCompute.h"
#include "Engine/Renderer/GBufferPass.h"
#include "Engine/Graphics/RenderBuffers.h"
+#include "Engine/Graphics/RenderView.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/GPUContext.h"
-#include "Engine/Serialization/Serialization.h"
+#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
+#include "Engine/Graphics/Shaders/GPUShader.h"
+#include "Engine/Serialization/Serialization.h"
#include "Engine/Level/Scene/SceneRendering.h"
PACK_STRUCT(struct Data {
diff --git a/Source/Engine/Level/Actors/Sky.h b/Source/Engine/Level/Actors/Sky.h
index c5c81edc0..eafd8d9e6 100644
--- a/Source/Engine/Level/Actors/Sky.h
+++ b/Source/Engine/Level/Actors/Sky.h
@@ -9,6 +9,8 @@
#include "Engine/Renderer/Config.h"
#include "Engine/Renderer/DrawCall.h"
+class GPUPipelineState;
+
///
/// Sky actor renders atmosphere around the scene with fog and sky.
///
diff --git a/Source/Engine/Level/Actors/SkyLight.cpp b/Source/Engine/Level/Actors/SkyLight.cpp
index 03e1d3ca5..da7efbf31 100644
--- a/Source/Engine/Level/Actors/SkyLight.cpp
+++ b/Source/Engine/Level/Actors/SkyLight.cpp
@@ -3,6 +3,7 @@
#include "SkyLight.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Graphics/RenderView.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/ProbesRenderer.h"
diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp
index ffa995f58..3e874f07f 100644
--- a/Source/Engine/Level/Actors/SplineModel.cpp
+++ b/Source/Engine/Level/Actors/SplineModel.cpp
@@ -5,9 +5,14 @@
#include "Engine/Engine/Engine.h"
#include "Engine/Core/Math/Matrix3x4.h"
#include "Engine/Serialization/Serialization.h"
+#include "Engine/Graphics/GPUBufferDescription.h"
#include "Engine/Graphics/GPUDevice.h"
+#include "Engine/Graphics/GPUBuffer.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Profiler/ProfilerCPU.h"
+#include "Engine/Renderer/DrawCall.h"
+#include "Engine/Renderer/RenderList.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#endif
diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp
index 3f81b22c3..0005e74f9 100644
--- a/Source/Engine/Level/Actors/StaticModel.cpp
+++ b/Source/Engine/Level/Actors/StaticModel.cpp
@@ -2,7 +2,10 @@
#include "StaticModel.h"
#include "Engine/Engine/Engine.h"
+#include "Engine/Graphics/GPUBuffer.h"
+#include "Engine/Graphics/GPUBufferDescription.h"
#include "Engine/Graphics/GPUDevice.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Level/Prefabs/PrefabManager.h"
#include "Engine/Level/Scene/Scene.h"
diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h
index a255ba4ab..b3276cc83 100644
--- a/Source/Engine/Level/Actors/StaticModel.h
+++ b/Source/Engine/Level/Actors/StaticModel.h
@@ -4,6 +4,7 @@
#include "ModelInstanceActor.h"
#include "Engine/Content/Assets/Model.h"
+#include "Engine/Renderer/DrawCall.h"
#include "Engine/Renderer/Lightmaps.h"
///
diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp
index 613427d9f..efc73dc63 100644
--- a/Source/Engine/Level/Level.cpp
+++ b/Source/Engine/Level/Level.cpp
@@ -10,6 +10,7 @@
#include "Engine/Core/Collections/CollectionPoolCache.h"
#include "Engine/Core/ObjectsRemovalService.h"
#include "Engine/Core/Config/LayersTagsSettings.h"
+#include "Engine/Core/Types/LayersMask.h"
#include "Engine/Debug/Exceptions/ArgumentException.h"
#include "Engine/Debug/Exceptions/ArgumentNullException.h"
#include "Engine/Debug/Exceptions/InvalidOperationException.h"
@@ -202,7 +203,7 @@ void LayersAndTagsSettings::Apply()
void LevelService::Update()
{
- PROFILE_CPU();
+ PROFILE_CPU_NAMED("Level::Update");
ScopeLock lock(Level::ScenesLock);
auto& scenes = Level::Scenes;
@@ -231,7 +232,7 @@ void LevelService::Update()
void LevelService::LateUpdate()
{
- PROFILE_CPU();
+ PROFILE_CPU_NAMED("Level::LateUpdate");
ScopeLock lock(Level::ScenesLock);
auto& scenes = Level::Scenes;
@@ -263,7 +264,7 @@ void LevelService::LateUpdate()
void LevelService::FixedUpdate()
{
- PROFILE_CPU();
+ PROFILE_CPU_NAMED("Level::FixedUpdate");
ScopeLock lock(Level::ScenesLock);
auto& scenes = Level::Scenes;
@@ -642,7 +643,7 @@ public:
void LevelImpl::CallSceneEvent(SceneEventType eventType, Scene* scene, Guid sceneId)
{
- PROFILE_CPU();
+ PROFILE_CPU_NAMED("Level::CallSceneEvent");
// Call event
const auto scriptsDomain = Scripting::GetScriptsDomain();
diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h
index 9fe9cb39b..1f7f0bb98 100644
--- a/Source/Engine/Level/Level.h
+++ b/Source/Engine/Level/Level.h
@@ -5,6 +5,7 @@
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Types/DateTime.h"
#include "Engine/Core/Types/DataContainer.h"
+#include "Engine/Core/Collections/Array.h"
#include "Engine/Platform/CriticalSection.h"
#include "Engine/Scripting/ScriptingType.h"
#include "Engine/Serialization/JsonFwd.h"
diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp
index a6a0edd4b..abad0c275 100644
--- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp
+++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp
@@ -6,6 +6,7 @@
#include "Engine/Core/ObjectsRemovalService.h"
#include "Engine/Core/Cache.h"
+#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h"
#include "Engine/Serialization/Json.h"
diff --git a/Source/Engine/Level/Prefabs/Prefab.cpp b/Source/Engine/Level/Prefabs/Prefab.cpp
index ed4ed2a2b..53bbb9d14 100644
--- a/Source/Engine/Level/Prefabs/Prefab.cpp
+++ b/Source/Engine/Level/Prefabs/Prefab.cpp
@@ -11,7 +11,7 @@
#include "Engine/Scripting/Scripting.h"
#endif
-REGISTER_JSON_ASSET(Prefab, "FlaxEngine.Prefab");
+REGISTER_JSON_ASSET(Prefab, "FlaxEngine.Prefab", false);
Prefab::Prefab(const SpawnParams& params, const AssetInfo* info)
: JsonAssetBase(params, info)
diff --git a/Source/Engine/Level/Prefabs/Prefab.h b/Source/Engine/Level/Prefabs/Prefab.h
index f5c25d273..694b50cdd 100644
--- a/Source/Engine/Level/Prefabs/Prefab.h
+++ b/Source/Engine/Level/Prefabs/Prefab.h
@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Content/JsonAsset.h"
+#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/Dictionary.h"
class Actor;
@@ -15,7 +16,6 @@ class SceneObject;
API_CLASS(NoSpawn) class FLAXENGINE_API Prefab : public JsonAssetBase
{
DECLARE_ASSET_HEADER(Prefab);
-
private:
bool _isCreatingDefaultInstance;
diff --git a/Source/Engine/Level/Scene/Lightmap.cpp b/Source/Engine/Level/Scene/Lightmap.cpp
index 4a2eec796..88fa88b5a 100644
--- a/Source/Engine/Level/Scene/Lightmap.cpp
+++ b/Source/Engine/Level/Scene/Lightmap.cpp
@@ -6,6 +6,7 @@
#include "Engine/Content/Content.h"
#include "Engine/Level/Level.h"
#include "Engine/Level/Scene/SceneLightmapsData.h"
+#include "Engine/Graphics/Textures/GPUTexture.h"
#if USE_EDITOR
#include "Engine/ContentImporters/ImportTexture.h"
#include "Engine/ContentImporters/AssetsImportingManager.h"
@@ -136,6 +137,14 @@ void Lightmap::EnsureSize(int32 size)
}
}
+bool Lightmap::IsReady() const
+{
+ // TODO: link for events and cache this to be a boolean value
+ return _textures[0] && _textures[0]->GetTexture()->ResidentMipLevels() > 0
+ && _textures[1] && _textures[1]->GetTexture()->ResidentMipLevels() > 0
+ && _textures[2] && _textures[2]->GetTexture()->ResidentMipLevels() > 0;
+}
+
#if USE_EDITOR
bool Lightmap::OnInitLightmap(TextureData& image)
diff --git a/Source/Engine/Level/Scene/Lightmap.h b/Source/Engine/Level/Scene/Lightmap.h
index c4550145d..6941f4323 100644
--- a/Source/Engine/Level/Scene/Lightmap.h
+++ b/Source/Engine/Level/Scene/Lightmap.h
@@ -96,14 +96,7 @@ public:
///
/// Determines whether this lightmap is ready (textures can be used by the renderer).
///
- /// True if lightmap textures are ready to use by renderer, otherwise false.
- FORCE_INLINE bool IsReady() const
- {
- // TODO: link for events and cache this to be a boolean value
- return _textures[0] && _textures[0]->GetTexture()->ResidentMipLevels() > 0
- && _textures[1] && _textures[1]->GetTexture()->ResidentMipLevels() > 0
- && _textures[2] && _textures[2]->GetTexture()->ResidentMipLevels() > 0;
- }
+ bool IsReady() const;
private:
diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp
index 94de9d713..fb9b4ce50 100644
--- a/Source/Engine/Level/Scene/Scene.cpp
+++ b/Source/Engine/Level/Scene/Scene.cpp
@@ -13,8 +13,11 @@
#include "Engine/Navigation/NavMesh.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Serialization/Serialization.h"
+#if USE_EDITOR
+#include "Engine/Engine/Globals.h"
+#endif
-REGISTER_JSON_ASSET(SceneAsset, "FlaxEngine.SceneAsset");
+REGISTER_JSON_ASSET(SceneAsset, "FlaxEngine.SceneAsset", false);
SceneAsset::SceneAsset(const SpawnParams& params, const AssetInfo* info)
: JsonAsset(params, info)
diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp
index 0fb96b735..ce93b3e1c 100644
--- a/Source/Engine/Level/Scene/SceneRendering.cpp
+++ b/Source/Engine/Level/Scene/SceneRendering.cpp
@@ -2,6 +2,7 @@
#include "SceneRendering.h"
#include "Scene.h"
+#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Level/Actors/PostFxVolume.h"
diff --git a/Source/Engine/Level/Scene/SceneTicking.h b/Source/Engine/Level/Scene/SceneTicking.h
index 8962f1810..e8fbe8236 100644
--- a/Source/Engine/Level/Scene/SceneTicking.h
+++ b/Source/Engine/Level/Scene/SceneTicking.h
@@ -8,7 +8,7 @@
///
/// Scene gameplay updating helper subsystem that boosts the level ticking by providing efficient objects cache.
///
-class SceneTicking
+class FLAXENGINE_API SceneTicking
{
friend Scene;
@@ -17,7 +17,7 @@ public:
///
/// Tick function type.
///
- struct Tick
+ struct FLAXENGINE_API Tick
{
typedef void (*Signature)();
typedef void (*SignatureObj)(void*);
@@ -51,7 +51,7 @@ public:
}
};
- class TickData
+ class FLAXENGINE_API TickData
{
public:
@@ -144,7 +144,7 @@ public:
}
};
- class FixedUpdateTickData : public TickData
+ class FLAXENGINE_API FixedUpdateTickData : public TickData
{
public:
@@ -156,7 +156,7 @@ public:
void TickScripts(const Array