BIN
Content/Shaders/BitonicSort.flax
LFS
BIN
Content/Shaders/BitonicSort.flax
LFS
Binary file not shown.
@@ -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.",
|
||||
|
||||
@@ -234,6 +234,7 @@
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/Name/@EntryValue">Deprecated</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/Pattern/@EntryValue">(?<=\W|^)(?<TAG>\[Deprecated)(\W|$)(.*)</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=EEA05B0ED8200E4BA9D2D3F1052EBFFD/TodoIconStyle/@EntryValue">Normal</s:String>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ackermann/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=analytics/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Antialiasing/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=backbuffer/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -53,6 +53,11 @@ namespace FlaxEditor.Content.Create
|
||||
/// </summary>
|
||||
NavigationSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The localization settings.
|
||||
/// </summary>
|
||||
LocalizationSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The build settings.
|
||||
/// </summary>
|
||||
@@ -92,6 +97,11 @@ namespace FlaxEditor.Content.Create
|
||||
/// The Android settings
|
||||
/// </summary>
|
||||
AndroidPlatformSettings,
|
||||
|
||||
/// <summary>
|
||||
/// The Switch settings
|
||||
/// </summary>
|
||||
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),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -21,9 +21,9 @@ namespace FlaxEditor.Content.Import
|
||||
public string OutputPath;
|
||||
|
||||
/// <summary>
|
||||
/// Flag set to true for binary assets handled by the engine internally.
|
||||
/// Flag set to true for the assets handled by the engine internally.
|
||||
/// </summary>
|
||||
public bool IsBinaryAsset;
|
||||
public bool IsInBuilt;
|
||||
|
||||
/// <summary>
|
||||
/// Flag used to skip showing import settings dialog to used. Can be used for importing assets from code by plugins.
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int MetadataToken => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is JsonAssetItem;
|
||||
return item is JsonAssetItem json && json.TypeName == TypeName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color AccentColor => Color.FromRGB(0xd14f67);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AcceptsAsset(string typeName, string path)
|
||||
{
|
||||
return typeName == TypeName && base.AcceptsAsset(typeName, path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AssetItem ConstructItem(string path, string typeName, ref Guid id)
|
||||
{
|
||||
@@ -143,6 +136,12 @@ namespace FlaxEditor.Content
|
||||
return path.EndsWith(FileExtension, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsProxyFor(ContentItem item)
|
||||
{
|
||||
return item is JsonAssetItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanCreate(ContentFolder targetLocation)
|
||||
{
|
||||
|
||||
24
Source/Editor/Content/Proxy/LocalizedStringTableProxy.cs
Normal file
24
Source/Editor/Content/Proxy/LocalizedStringTableProxy.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="LocalizedStringTable"/> proxy.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Content.JsonAssetProxy" />
|
||||
public class LocalizedStringTableProxy : JsonAssetProxy
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||
{
|
||||
return new LocalizedStringTableWindow(editor, (JsonAssetItem)item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string TypeName => "FlaxEngine.LocalizedStringTable";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Android ARM64 (arm64-v8a)\")")
|
||||
AndroidARM64 = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Switch.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
|
||||
/// <summary>
|
||||
/// The output path.
|
||||
/// The output path for data files (Content, Mono, etc.).
|
||||
/// </summary>
|
||||
String OutputPath;
|
||||
String DataOutputPath;
|
||||
|
||||
/// <summary>
|
||||
/// The output path for binaries (executable and code libraries).
|
||||
/// </summary>
|
||||
String CodeOutputPath;
|
||||
|
||||
/// <summary>
|
||||
/// The platform tools.
|
||||
|
||||
@@ -39,7 +39,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 +46,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 +91,50 @@ using namespace GameCookerImpl;
|
||||
Delegate<GameCooker::EventType> GameCooker::OnEvent;
|
||||
Delegate<const String&, float> 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 +296,11 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform)
|
||||
case BuildPlatform::AndroidARM64:
|
||||
result = New<AndroidPlatformTools>(ArchitectureType::ARM64);
|
||||
break;
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_SWITCH
|
||||
case BuildPlatform::Switch:
|
||||
result = New<SwitchPlatformTools>();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
Tools.Add(platform, result);
|
||||
@@ -282,9 +333,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 +419,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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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."));
|
||||
@@ -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))
|
||||
@@ -176,7 +176,7 @@ 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))
|
||||
{
|
||||
@@ -205,7 +205,7 @@ 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
|
||||
@@ -264,7 +264,7 @@ 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))
|
||||
{
|
||||
@@ -297,7 +297,7 @@ 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
|
||||
@@ -347,7 +347,7 @@ 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))
|
||||
{
|
||||
@@ -484,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))
|
||||
|
||||
@@ -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<String> files;
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -453,6 +453,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 +869,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 +1035,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 +1129,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;
|
||||
|
||||
@@ -15,7 +15,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 +26,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))
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -250,6 +250,15 @@ namespace FlaxEditor.CustomEditors
|
||||
_children[i].RefreshInternal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the value of the <see cref="Values"/> container. Called during Refresh to flush property after editing it in UI.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set.</param>
|
||||
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;
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace FlaxEditor.CustomEditors
|
||||
/// <inheritdoc />
|
||||
protected override void OnModified()
|
||||
{
|
||||
Presenter.Modified?.Invoke();
|
||||
Presenter.OnModified();
|
||||
|
||||
base.OnModified();
|
||||
}
|
||||
@@ -354,6 +354,14 @@ namespace FlaxEditor.CustomEditors
|
||||
ExpandGroups(this, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes <see cref="Modified"/> event.
|
||||
/// </summary>
|
||||
public void OnModified()
|
||||
{
|
||||
Modified?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when selection gets changed.
|
||||
/// </summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc />
|
||||
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<LocalizedStringTable>(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<LocalizedStringTable, Dictionary<string, string[]>>();
|
||||
var allKeys = new HashSet<string>();
|
||||
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<LocalizedStringTable>();
|
||||
table.Locale = culture.Name;
|
||||
var entries = new Dictionary<string, string[]>();
|
||||
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<LocalizedStringTable>(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<LocalizedStringTable>();
|
||||
table.Locale = culture.Name;
|
||||
if (!table.Save(path))
|
||||
{
|
||||
Object.Destroy(table);
|
||||
newTables.Add(FlaxEngine.Content.LoadAsync<LocalizedStringTable>(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<string>();
|
||||
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<string, string>();
|
||||
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<string, string>();
|
||||
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<string, string> newKeys, HashSet<string> allKeys)
|
||||
{
|
||||
var startToken = "Localization.GetString";
|
||||
var textToken = "\"";
|
||||
FindNewKeys(file, newKeys, allKeys, startToken, textToken);
|
||||
}
|
||||
|
||||
private static void FindNewKeysCpp(string file, Dictionary<string, string> newKeys, HashSet<string> allKeys)
|
||||
{
|
||||
var startToken = "Localization::GetString";
|
||||
var textToken = "TEXT(\"";
|
||||
FindNewKeys(file, newKeys, allKeys, startToken, textToken);
|
||||
}
|
||||
|
||||
private static void FindNewKeys(string file, Dictionary<string, string> newKeys, HashSet<string> 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<string, string> newKeys, HashSet<string> 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<string, string> newKeys, HashSet<string> 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<string, string> newKeys, int filesCount, IEnumerable<IGrouping<string, LocalizedStringTable>> locales, Dictionary<LocalizedStringTable, Dictionary<string, string[]>> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
/// <summary>
|
||||
/// Context menu for anchors presets editing.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.ContextMenu.ContextMenuBase" />
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AnchorPresetsEditorPopup"/> class.
|
||||
/// </summary>
|
||||
/// <param name="presets">The initial value.</param>
|
||||
public AnchorPresetsEditorPopup(AnchorPresets presets)
|
||||
/// <param name="supportsShiftModulation">If the popup should react to shift</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
@@ -311,7 +404,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// Dedicated custom editor for <see cref="UIControl.Control"/> object.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.Editors.GenericEditor" />
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
164
Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs
Normal file
164
Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit <see cref="CultureInfo"/> value type properties. Supports editing property of <see cref="string"/> type (as culture name).
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(CultureInfo)), DefaultEditor]
|
||||
internal class CultureInfoEditor : CustomEditor
|
||||
{
|
||||
private ClickableLabel _label;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
_label.Text = GetName(Culture);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<CultureInfo>
|
||||
{
|
||||
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<CultureInfo> 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<TreeNode>();
|
||||
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<int, ContainerControl>();
|
||||
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<TreeNode> before, List<TreeNode> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -9,7 +9,7 @@ using FlaxEngine;
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit float value type properties.
|
||||
/// Default implementation of the inspector used to edit enum value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Enum)), DefaultEditor]
|
||||
public class EnumEditor : CustomEditor
|
||||
|
||||
@@ -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)
|
||||
|
||||
234
Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs
Normal file
234
Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit localized string properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(LocalizedString)), DefaultEditor]
|
||||
public sealed class LocalizedStringEditor : GenericEditor
|
||||
{
|
||||
private TextBoxElement _idElement, _valueElement;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override void RefreshInternal()
|
||||
{
|
||||
base.RefreshInternal();
|
||||
|
||||
if (_valueElement != null)
|
||||
{
|
||||
_valueElement.TextBox.WatermarkText = Localization.GetString(_idElement.Text);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
base.Deinitialize();
|
||||
|
||||
_idElement = null;
|
||||
_valueElement = null;
|
||||
}
|
||||
|
||||
private void OnSelectStringClicked(Button button)
|
||||
{
|
||||
var settings = GameSettings.Load<LocalizationSettings>();
|
||||
if (settings?.LocalizedStringTables == null || settings.LocalizedStringTables.Length == 0)
|
||||
{
|
||||
MessageBox.Show("No valid localization settings setup.");
|
||||
return;
|
||||
}
|
||||
Profiler.BeginEvent("LocalizedStringEditor.OnSelectStringClicked");
|
||||
var allKeys = new HashSet<string>();
|
||||
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<TreeNode> before, List<TreeNode> 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<LocalizationSettings>();
|
||||
if (settings?.LocalizedStringTables == null || settings.LocalizedStringTables.Length == 0)
|
||||
{
|
||||
MessageBox.Show("No valid localization settings setup.");
|
||||
return;
|
||||
}
|
||||
Profiler.BeginEvent("LocalizedStringEditor.OnAddStringClicked");
|
||||
var allKeys = new HashSet<string>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
using System;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
[CustomEditor(typeof(Quaternion)), DefaultEditor]
|
||||
public class QuaternionEditor : CustomEditor
|
||||
{
|
||||
private Vector3 _cachedAngles = Vector3.Zero;
|
||||
private object _cachedToken;
|
||||
|
||||
/// <summary>
|
||||
/// The X component element
|
||||
/// </summary>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void ClearToken()
|
||||
{
|
||||
_cachedToken = null;
|
||||
base.ClearToken();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
|
||||
@@ -5,15 +5,15 @@ using FlaxEngine.GUI;
|
||||
namespace FlaxEditor.CustomEditors.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// The vertical panel element.
|
||||
/// The horizontal panel element.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.LayoutElement" />
|
||||
public class VerticalPanelElement : LayoutElementsContainer
|
||||
public class HorizontalPanelElement : LayoutElementsContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The panel.
|
||||
/// </summary>
|
||||
public readonly VerticalPanel Panel = new VerticalPanel();
|
||||
public readonly HorizontalPanel Panel = new HorizontalPanel();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContainerControl ContainerControl => Panel;
|
||||
|
||||
@@ -5,15 +5,15 @@ using FlaxEngine.GUI;
|
||||
namespace FlaxEditor.CustomEditors.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// The horizontal panel element.
|
||||
/// The vertical panel element.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.LayoutElement" />
|
||||
public class HorizontalPanelElement : LayoutElementsContainer
|
||||
public class VerticalPanelElement : LayoutElementsContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The panel.
|
||||
/// </summary>
|
||||
public readonly HorizontalPanel Panel = new HorizontalPanel();
|
||||
public readonly VerticalPanel Panel = new VerticalPanel();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ContainerControl ContainerControl => Panel;
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace FlaxEditor.CustomEditors
|
||||
OnAddElement(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds new horizontal panel element.
|
||||
/// </summary>
|
||||
@@ -690,6 +690,17 @@ namespace FlaxEditor.CustomEditors
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds custom element to the layout.
|
||||
/// </summary>
|
||||
/// <param name="element">The element.</param>
|
||||
public void AddElement(LayoutElement element)
|
||||
{
|
||||
if (element == null)
|
||||
throw new ArgumentNullException();
|
||||
OnAddElement(element);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when element is added to the layout.
|
||||
/// </summary>
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -752,10 +752,11 @@ namespace FlaxEditor
|
||||
/// <param name="type">The collision data type.</param>
|
||||
/// <param name="model">The source model.</param>
|
||||
/// <param name="modelLodIndex">The source model LOD index.</param>
|
||||
/// <param name="materialSlotsMask">The source model material slots mask. One bit per-slot. Can be sued to exclude particular material slots from collision cooking.</param>
|
||||
/// <param name="convexFlags">The convex mesh generation flags.</param>
|
||||
/// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -877,10 +878,12 @@ namespace FlaxEditor
|
||||
/// Checks if can import asset with the given extension.
|
||||
/// </summary>
|
||||
/// <param name="extension">The file extension.</param>
|
||||
/// <param name="outputExtension">The output file extension (flax, json, etc.).</param>
|
||||
/// <returns>True if can import files with given extension, otherwise false.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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);
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// The cached value from the UI.
|
||||
/// </summary>
|
||||
protected int _cachedValue;
|
||||
protected ulong _cachedValue;
|
||||
|
||||
/// <summary>
|
||||
/// True if has value cached, otherwise false.
|
||||
@@ -54,7 +54,7 @@ namespace FlaxEditor.GUI
|
||||
/// <summary>
|
||||
/// The value.
|
||||
/// </summary>
|
||||
public int Value;
|
||||
public ulong Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Entry"/> struct.
|
||||
@@ -62,7 +62,7 @@ namespace FlaxEditor.GUI
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="tooltip">The tooltip.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public Entry(string name, int value, string tooltip = null)
|
||||
public Entry(string name, ulong 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.ToUInt64(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
public int Value
|
||||
public ulong Value
|
||||
{
|
||||
get => _cachedValue;
|
||||
set
|
||||
@@ -209,13 +209,13 @@ namespace FlaxEditor.GUI
|
||||
/// </summary>
|
||||
protected void CacheValue()
|
||||
{
|
||||
int value = 0;
|
||||
ulong 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.ToUInt64(field.GetRawConstantValue()), tooltip));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,9 +295,9 @@ namespace FlaxEditor.GUI
|
||||
}
|
||||
|
||||
// Calculate value that will be set after change
|
||||
int valueAfter = 0;
|
||||
ulong valueAfter = 0;
|
||||
bool isSelected = _selectedIndices.Contains(index);
|
||||
int selectedValue = entries[index].Value;
|
||||
ulong selectedValue = entries[index].Value;
|
||||
for (int i = 0; i < _selectedIndices.Count; i++)
|
||||
{
|
||||
int selectedIndex = _selectedIndices[i];
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace FlaxEditor.GUI
|
||||
new PlatformData(PlatformType.PS4, icons.PS4, "PlayStation 4"),
|
||||
new PlatformData(PlatformType.XboxScarlett, icons.XboxSeriesX, "Xbox Scarlett"),
|
||||
new PlatformData(PlatformType.Android, icons.Android, "Android"),
|
||||
new PlatformData(PlatformType.Switch, icons.ColorWheel, "Switch"),
|
||||
};
|
||||
|
||||
const float IconSize = 48.0f;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ActionString { get; }
|
||||
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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<PhysicalMaterial>());
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -243,10 +241,10 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The input path.</param>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="isBinaryAsset">True if output file is a binary asset.</param>
|
||||
/// <param name="isInBuilt">True if use in-built importer (engine backend).</param>
|
||||
/// <param name="skipSettingsDialog">True if skip any popup dialogs showing for import options adjusting. Can be used when importing files from code.</param>
|
||||
/// <param name="settings">Import settings to override. Use null to skip this value.</param>
|
||||
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,
|
||||
});
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action called after pasting actor in editor.
|
||||
/// </summary>
|
||||
public virtual void PostPaste()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
|
||||
@@ -25,5 +25,20 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
if (Actor is UIControl uiControl)
|
||||
DebugDraw.DrawWireBox(uiControl.Bounds, Color.BlueViolet);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PostPaste()
|
||||
{
|
||||
base.PostPaste();
|
||||
|
||||
var control = ((UIControl)Actor).Control;
|
||||
if (control != null)
|
||||
{
|
||||
if (control.Parent != null)
|
||||
control.Parent.PerformLayout();
|
||||
else
|
||||
control.PerformLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,10 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
}
|
||||
parent.SortChildren();
|
||||
}
|
||||
else if (Actor)
|
||||
{
|
||||
_orderInParent = Actor.OrderInParent;
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnNameChanged()
|
||||
|
||||
@@ -112,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();
|
||||
}
|
||||
}
|
||||
@@ -374,6 +365,7 @@ namespace FlaxEditor.SceneGraph
|
||||
/// <summary>
|
||||
/// Gets or sets the node state.
|
||||
/// </summary>
|
||||
[NoSerialize]
|
||||
public virtual StateData State
|
||||
{
|
||||
get => throw new NotImplementedException();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -672,6 +672,24 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
private class ThisNode : SurfaceNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ThisNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -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),
|
||||
|
||||
@@ -32,13 +32,13 @@ namespace FlaxEditor.Surface.Elements
|
||||
Width = archetype.Size.X;
|
||||
ParentNode = parentNode;
|
||||
Archetype = archetype;
|
||||
Value = (int)ParentNode.Values[Archetype.ValueIndex];
|
||||
Value = (ulong)(int)ParentNode.Values[Archetype.ValueIndex];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Gets or sets the serialized undo data.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The data.
|
||||
/// </value>
|
||||
[NoSerialize]
|
||||
public TData Data
|
||||
public virtual TData Data
|
||||
{
|
||||
get => JsonConvert.DeserializeObject<TData>(_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<TData>(_data, FlaxEngine.Json.JsonSerializer.Settings);
|
||||
protected set => _data = JsonConvert.SerializeObject(value, Formatting.None, FlaxEngine.Json.JsonSerializer.Settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -6,6 +6,8 @@ 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;
|
||||
@@ -67,6 +69,10 @@ namespace FlaxEditor.Utilities
|
||||
Dictionary,
|
||||
ManagedObject,
|
||||
Typename,
|
||||
|
||||
Int2,
|
||||
Int3,
|
||||
Int4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -446,6 +452,12 @@ namespace FlaxEditor.Utilities
|
||||
variantType = VariantType.Vector3;
|
||||
else if (type == typeof(Vector4))
|
||||
variantType = VariantType.Vector4;
|
||||
else if (type == typeof(Int2))
|
||||
variantType = VariantType.Int2;
|
||||
else if (type == typeof(Int3))
|
||||
variantType = VariantType.Int3;
|
||||
else if (type == typeof(Int4))
|
||||
variantType = VariantType.Int4;
|
||||
else if (type == typeof(Color))
|
||||
variantType = VariantType.Color;
|
||||
else if (type == typeof(Guid))
|
||||
@@ -682,6 +694,21 @@ namespace FlaxEditor.Utilities
|
||||
new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()));
|
||||
break;
|
||||
}
|
||||
case 19: // CommonType::Int2
|
||||
{
|
||||
value = stream.ReadInt2();
|
||||
break;
|
||||
}
|
||||
case 20: // CommonType::Int3
|
||||
{
|
||||
value = stream.ReadInt3();
|
||||
break;
|
||||
}
|
||||
case 21: // CommonType::Int4
|
||||
{
|
||||
value = stream.ReadInt4();
|
||||
break;
|
||||
}
|
||||
default: throw new SystemException();
|
||||
}
|
||||
}
|
||||
@@ -733,6 +760,9 @@ namespace FlaxEditor.Utilities
|
||||
case VariantType.Vector2: return new ScriptType(typeof(Vector2));
|
||||
case VariantType.Vector3: return new ScriptType(typeof(Vector3));
|
||||
case VariantType.Vector4: return new ScriptType(typeof(Vector4));
|
||||
case VariantType.Int2: return new ScriptType(typeof(Int2));
|
||||
case VariantType.Int3: return new ScriptType(typeof(Int3));
|
||||
case VariantType.Int4: return new ScriptType(typeof(Int4));
|
||||
case VariantType.Color: return new ScriptType(typeof(Color));
|
||||
case VariantType.Guid: return new ScriptType(typeof(Guid));
|
||||
case VariantType.BoundingBox: return new ScriptType(typeof(BoundingBox));
|
||||
@@ -797,6 +827,9 @@ namespace FlaxEditor.Utilities
|
||||
case VariantType.Vector2: return typeof(Vector2);
|
||||
case VariantType.Vector3: return typeof(Vector3);
|
||||
case VariantType.Vector4: return typeof(Vector4);
|
||||
case VariantType.Int2: return typeof(Int2);
|
||||
case VariantType.Int3: return typeof(Int3);
|
||||
case VariantType.Int4: return typeof(Int4);
|
||||
case VariantType.Color: return typeof(Color);
|
||||
case VariantType.Guid: return typeof(Guid);
|
||||
case VariantType.BoundingBox: return typeof(BoundingBox);
|
||||
@@ -894,6 +927,9 @@ namespace FlaxEditor.Utilities
|
||||
case VariantType.Vector2: return stream.ReadVector2();
|
||||
case VariantType.Vector3: return stream.ReadVector3();
|
||||
case VariantType.Vector4: return stream.ReadVector4();
|
||||
case VariantType.Int2: return stream.ReadInt2();
|
||||
case VariantType.Int3: return stream.ReadInt3();
|
||||
case VariantType.Int4: return stream.ReadInt4();
|
||||
case VariantType.Color: return stream.ReadColor();
|
||||
case VariantType.Guid: return stream.ReadGuid();
|
||||
case VariantType.BoundingBox: return stream.ReadBoundingBox();
|
||||
@@ -1087,6 +1123,21 @@ namespace FlaxEditor.Utilities
|
||||
stream.Write(asRay.Direction.Y);
|
||||
stream.Write(asRay.Direction.Z);
|
||||
}
|
||||
else if (value is Int2 asInt2)
|
||||
{
|
||||
stream.Write((byte)19);
|
||||
stream.Write(asInt2);
|
||||
}
|
||||
else if (value is Int3 asInt3)
|
||||
{
|
||||
stream.Write((byte)20);
|
||||
stream.Write(asInt3);
|
||||
}
|
||||
else if (value is Int4 asInt4)
|
||||
{
|
||||
stream.Write((byte)21);
|
||||
stream.Write(asInt4);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException(string.Format("Invalid Common Value type {0}", value != null ? value.GetType().ToString() : "null"));
|
||||
@@ -1194,6 +1245,15 @@ namespace FlaxEditor.Utilities
|
||||
case VariantType.Vector4:
|
||||
stream.Write((Vector4)value);
|
||||
break;
|
||||
case VariantType.Int2:
|
||||
stream.Write((Int2)value);
|
||||
break;
|
||||
case VariantType.Int3:
|
||||
stream.Write((Int3)value);
|
||||
break;
|
||||
case VariantType.Int4:
|
||||
stream.Write((Int4)value);
|
||||
break;
|
||||
case VariantType.Color:
|
||||
stream.Write((Color)value);
|
||||
break;
|
||||
@@ -1386,6 +1446,51 @@ namespace FlaxEditor.Utilities
|
||||
stream.WriteEndObject();
|
||||
break;
|
||||
}
|
||||
case VariantType.Int2:
|
||||
{
|
||||
var asInt2 = (Int2)value;
|
||||
stream.WriteStartObject();
|
||||
|
||||
stream.WritePropertyName("X");
|
||||
stream.WriteValue(asInt2.X);
|
||||
stream.WritePropertyName("Y");
|
||||
stream.WriteValue(asInt2.Y);
|
||||
|
||||
stream.WriteEndObject();
|
||||
break;
|
||||
}
|
||||
case VariantType.Int3:
|
||||
{
|
||||
var asInt3 = (Int3)value;
|
||||
stream.WriteStartObject();
|
||||
|
||||
stream.WritePropertyName("X");
|
||||
stream.WriteValue(asInt3.X);
|
||||
stream.WritePropertyName("Y");
|
||||
stream.WriteValue(asInt3.Y);
|
||||
stream.WritePropertyName("Z");
|
||||
stream.WriteValue(asInt3.Z);
|
||||
|
||||
stream.WriteEndObject();
|
||||
break;
|
||||
}
|
||||
case VariantType.Int4:
|
||||
{
|
||||
var asInt4 = (Int4)value;
|
||||
stream.WriteStartObject();
|
||||
|
||||
stream.WritePropertyName("X");
|
||||
stream.WriteValue(asInt4.X);
|
||||
stream.WritePropertyName("Y");
|
||||
stream.WriteValue(asInt4.Y);
|
||||
stream.WritePropertyName("Z");
|
||||
stream.WriteValue(asInt4.Z);
|
||||
stream.WritePropertyName("W");
|
||||
stream.WriteValue(asInt4.W);
|
||||
|
||||
stream.WriteEndObject();
|
||||
break;
|
||||
}
|
||||
case VariantType.Color:
|
||||
{
|
||||
var asColor = (Color)value;
|
||||
@@ -1777,5 +1882,41 @@ namespace FlaxEditor.Utilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the search popup with a tree.
|
||||
/// </summary>
|
||||
/// <param name="searchBox">The search box.</param>
|
||||
/// <param name="tree">The tree control.</param>
|
||||
/// <returns>The created menu to setup and show.</returns>
|
||||
public static ContextMenuBase CreateSearchPopup(out TextBox searchBox, out Tree tree)
|
||||
{
|
||||
var menu = new ContextMenuBase
|
||||
{
|
||||
Size = new Vector2(320, 220),
|
||||
};
|
||||
searchBox = new TextBox(false, 1, 1)
|
||||
{
|
||||
Width = menu.Width - 3,
|
||||
WatermarkText = "Search...",
|
||||
Parent = menu,
|
||||
};
|
||||
var panel1 = new Panel(ScrollBars.Vertical)
|
||||
{
|
||||
Bounds = new Rectangle(0, searchBox.Bottom + 1, menu.Width, menu.Height - searchBox.Bottom - 2),
|
||||
Parent = menu
|
||||
};
|
||||
var panel2 = new VerticalPanel
|
||||
{
|
||||
Parent = panel1,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
IsScrollable = true,
|
||||
};
|
||||
tree = new Tree(false)
|
||||
{
|
||||
Parent = panel2,
|
||||
};
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,7 +974,8 @@ namespace FlaxEditor.Viewport
|
||||
// Get input buttons and keys (skip if viewport has no focus or mouse is over a child control)
|
||||
bool useMouse = Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height);
|
||||
_prevInput = _input;
|
||||
if (ContainsFocus && GetChildAt(_viewMousePos, c => c.Visible) == null)
|
||||
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl));
|
||||
if (ContainsFocus && hit == null)
|
||||
_input.Gather(win.Window, useMouse);
|
||||
else
|
||||
_input.Clear();
|
||||
|
||||
122
Source/Editor/Viewport/Previews/ModelBasePreview.cs
Normal file
122
Source/Editor/Viewport/Previews/ModelBasePreview.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Model Base asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class ModelBasePreview : AssetPreview
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the model asset to preview.
|
||||
/// </summary>
|
||||
public ModelBase Asset
|
||||
{
|
||||
get => (ModelBase)StaticModel.Model ?? AnimatedModel.SkinnedModel;
|
||||
set
|
||||
{
|
||||
StaticModel.Model = value as Model;
|
||||
AnimatedModel.SkinnedModel = value as SkinnedModel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The static model for display.
|
||||
/// </summary>
|
||||
public StaticModel StaticModel;
|
||||
|
||||
/// <summary>
|
||||
/// The animated model for display.
|
||||
/// </summary>
|
||||
public AnimatedModel AnimatedModel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether scale the model to the normalized bounds.
|
||||
/// </summary>
|
||||
public bool ScaleToFit { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelBasePreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||
public ModelBasePreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
Task.Begin += OnBegin;
|
||||
|
||||
// Setup preview scene
|
||||
StaticModel = new StaticModel();
|
||||
AnimatedModel = new AnimatedModel();
|
||||
|
||||
// Link actors for rendering
|
||||
Task.AddCustomActor(StaticModel);
|
||||
Task.AddCustomActor(AnimatedModel);
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
// Preview LOD
|
||||
{
|
||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
||||
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
|
||||
{
|
||||
Parent = previewLOD
|
||||
};
|
||||
previewLODValue.ValueChanged += () =>
|
||||
{
|
||||
StaticModel.ForcedLOD = previewLODValue.Value;
|
||||
AnimatedModel.ForcedLOD = previewLODValue.Value;
|
||||
};
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = StaticModel.ForcedLOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
{
|
||||
var position = Vector3.Zero;
|
||||
var scale = Vector3.One;
|
||||
|
||||
// Update preview model scale to fit the preview
|
||||
var model = Asset;
|
||||
if (ScaleToFit && model && model.IsLoaded)
|
||||
{
|
||||
float targetSize = 50.0f;
|
||||
BoundingBox box = model is Model ? ((Model)model).GetBox() : ((SkinnedModel)model).GetBox();
|
||||
float maxSize = Mathf.Max(0.001f, box.Size.MaxValue);
|
||||
scale = new Vector3(targetSize / maxSize);
|
||||
position = box.Center * (-0.5f * scale.X) + new Vector3(0, -10, 0);
|
||||
}
|
||||
|
||||
StaticModel.Transform = new Transform(position, StaticModel.Orientation, scale);
|
||||
AnimatedModel.Transform = new Transform(position, AnimatedModel.Orientation, scale);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.F:
|
||||
// Pay respect..
|
||||
ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
|
||||
break;
|
||||
}
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Ensure to cleanup created actor objects
|
||||
Object.Destroy(ref StaticModel);
|
||||
Object.Destroy(ref AnimatedModel);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Content.Create;
|
||||
@@ -10,6 +11,7 @@ using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
@@ -20,6 +22,15 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
||||
public sealed class CollisionDataWindow : AssetEditorWindowBase<CollisionData>
|
||||
{
|
||||
[Flags]
|
||||
private enum MaterialSlotsMask : uint
|
||||
{
|
||||
// @formatter:off
|
||||
Slot0=1u<<0,Slot1=1u<<1,Slot2=1u<<2,Slot3=1u<<3,Slot4=1u<<4,Slot5=1u<<5,Slot6=1u<<6,Slot7=1u<<7,Slot8=1u<<8,Slot9=1u<<9,Slot10=1u<<10,Slot11=1u<<11,Slot12=1u<<12,Slot13=1u<<13,Slot14=1u<<14,Slot15=1u<<15,Slot16=1u<<16,Slot17=1u<<17,Slot18=1u<<18,Slot19=1u<<19,Slot20=1u<<20,Slot21=1u<<21,Slot22=1u<<22,Slot23=1u<<23,Slot24=1u<<24,Slot25=1u<<25,Slot26=1u<<26,Slot27=1u<<27,Slot28=1u<<28,Slot29=1u<<29,Slot30=1u<<30,Slot31=1u<<31,
|
||||
// @formatter:on
|
||||
All = uint.MaxValue,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The asset properties proxy object.
|
||||
/// </summary>
|
||||
@@ -34,11 +45,14 @@ namespace FlaxEditor.Windows.Assets
|
||||
public CollisionDataType Type;
|
||||
|
||||
[EditorOrder(10), EditorDisplay("General"), Tooltip("Source model asset to use for collision data generation")]
|
||||
public Model Model;
|
||||
public ModelBase Model;
|
||||
|
||||
[EditorOrder(20), Limit(0, 5), EditorDisplay("General", "Model LOD Index"), Tooltip("Source model LOD index to use for collision data generation (will be clamped to the actual model LODs collection size)")]
|
||||
public int ModelLodIndex;
|
||||
|
||||
[EditorOrder(30), EditorDisplay("General"), Tooltip("The source model material slots mask. One bit per-slot. Can be sued to exclude particular material slots from collision cooking.")]
|
||||
public MaterialSlotsMask MaterialSlotsMask = MaterialSlotsMask.All;
|
||||
|
||||
[EditorOrder(100), EditorDisplay("Convex Mesh", "Convex Flags"), Tooltip("Convex mesh generation flags")]
|
||||
public ConvexMeshGenerationFlags ConvexFlags;
|
||||
|
||||
@@ -88,28 +102,23 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
private class CookData : CreateFileEntry
|
||||
{
|
||||
private PropertiesProxy Proxy;
|
||||
private CollisionDataType Type;
|
||||
private Model Model;
|
||||
private int ModelLodIndex;
|
||||
private ConvexMeshGenerationFlags ConvexFlags;
|
||||
private int ConvexVertexLimit;
|
||||
public PropertiesProxy Proxy;
|
||||
public CollisionDataType Type;
|
||||
public ModelBase Model;
|
||||
public int ModelLodIndex;
|
||||
public uint MaterialSlotsMask;
|
||||
public ConvexMeshGenerationFlags ConvexFlags;
|
||||
public int ConvexVertexLimit;
|
||||
|
||||
public CookData(PropertiesProxy proxy, string resultUrl, CollisionDataType type, Model model, int modelLodIndex, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit)
|
||||
public CookData(string resultUrl)
|
||||
: base("Collision Data", resultUrl)
|
||||
{
|
||||
Proxy = proxy;
|
||||
Type = type;
|
||||
Model = model;
|
||||
ModelLodIndex = modelLodIndex;
|
||||
ConvexFlags = convexFlags;
|
||||
ConvexVertexLimit = convexVertexLimit;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Create()
|
||||
{
|
||||
bool failed = FlaxEditor.Editor.CookMeshCollision(ResultUrl, Type, Model, ModelLodIndex, ConvexFlags, ConvexVertexLimit);
|
||||
bool failed = FlaxEditor.Editor.CookMeshCollision(ResultUrl, Type, Model, ModelLodIndex, MaterialSlotsMask, ConvexFlags, ConvexVertexLimit);
|
||||
|
||||
Proxy._isCooking = false;
|
||||
Proxy.Window.UpdateWiresModel();
|
||||
@@ -121,7 +130,16 @@ namespace FlaxEditor.Windows.Assets
|
||||
public void Cook()
|
||||
{
|
||||
_isCooking = true;
|
||||
Window.Editor.ContentImporting.Create(new CookData(this, Asset.Path, Type, Model, ModelLodIndex, ConvexFlags, ConvexVertexLimit));
|
||||
Window.Editor.ContentImporting.Create(new CookData(Asset.Path)
|
||||
{
|
||||
Proxy = this,
|
||||
Type = Type,
|
||||
Model = Model,
|
||||
ModelLodIndex = ModelLodIndex,
|
||||
MaterialSlotsMask = (uint)MaterialSlotsMask,
|
||||
ConvexFlags = ConvexFlags,
|
||||
ConvexVertexLimit = ConvexVertexLimit,
|
||||
});
|
||||
}
|
||||
|
||||
public void OnLoad(CollisionDataWindow window)
|
||||
@@ -135,7 +153,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
Type = options.Type;
|
||||
if (Type == CollisionDataType.None)
|
||||
Type = CollisionDataType.ConvexMesh;
|
||||
Model = FlaxEngine.Content.LoadAsync<Model>(options.Model);
|
||||
Model = FlaxEngine.Content.LoadAsync<ModelBase>(options.Model);
|
||||
ModelLodIndex = options.ModelLodIndex;
|
||||
ConvexFlags = options.ConvexFlags;
|
||||
ConvexVertexLimit = options.ConvexVertexLimit;
|
||||
@@ -151,7 +169,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
|
||||
private readonly SplitPanel _split;
|
||||
private readonly ModelPreview _preview;
|
||||
private readonly ModelBasePreview _preview;
|
||||
private readonly CustomEditorPresenter _propertiesPresenter;
|
||||
private readonly PropertiesProxy _properties;
|
||||
private Model _collisionWiresModel;
|
||||
@@ -176,7 +194,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
};
|
||||
|
||||
// Model preview
|
||||
_preview = new ModelPreview(true)
|
||||
_preview = new ModelBasePreview(true)
|
||||
{
|
||||
ViewportCamera = new FPSCamera(),
|
||||
Parent = _split.Panel1
|
||||
@@ -195,7 +213,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Sync helper actor size with actual preview model (preview scales model for better usage experience)
|
||||
if (_collisionWiresShowActor && _collisionWiresShowActor.IsActive)
|
||||
{
|
||||
_collisionWiresShowActor.Transform = _preview.PreviewActor.Transform;
|
||||
_collisionWiresShowActor.Transform = _preview.StaticModel.Transform;
|
||||
}
|
||||
|
||||
base.Update(deltaTime);
|
||||
@@ -230,14 +248,14 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
_collisionWiresShowActor.Model = _collisionWiresModel;
|
||||
_collisionWiresShowActor.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.WiresDebugMaterial));
|
||||
_preview.Model = FlaxEngine.Content.LoadAsync<Model>(_asset.Options.Model);
|
||||
_preview.Asset = FlaxEngine.Content.LoadAsync<ModelBase>(_asset.Options.Model);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UnlinkItem()
|
||||
{
|
||||
_properties.OnClean();
|
||||
_preview.Model = null;
|
||||
_preview.Asset = null;
|
||||
|
||||
base.UnlinkItem();
|
||||
}
|
||||
@@ -245,7 +263,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <inheritdoc />
|
||||
protected override void OnAssetLinked()
|
||||
{
|
||||
_preview.Model = null;
|
||||
_preview.Asset = null;
|
||||
|
||||
base.OnAssetLinked();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
private readonly CustomEditorPresenter _presenter;
|
||||
private readonly ToolStripButton _saveButton;
|
||||
private readonly ToolStripButton _undoButton;
|
||||
private readonly ToolStripButton _redoButton;
|
||||
private readonly Undo _undo;
|
||||
private object _object;
|
||||
private bool _isRegisteredForScriptsReload;
|
||||
|
||||
@@ -24,8 +27,16 @@ namespace FlaxEditor.Windows.Assets
|
||||
public JsonAssetWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
_undo.RedoDone += OnUndoRedo;
|
||||
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save32, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo32, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo32, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
|
||||
// Panel
|
||||
var panel = new Panel(ScrollBars.Vertical)
|
||||
@@ -36,9 +47,19 @@ namespace FlaxEditor.Windows.Assets
|
||||
};
|
||||
|
||||
// Properties
|
||||
_presenter = new CustomEditorPresenter(null, "Loading...");
|
||||
_presenter = new CustomEditorPresenter(_undo, "Loading...");
|
||||
_presenter.Panel.Parent = panel;
|
||||
_presenter.Modified += MarkAsEdited;
|
||||
|
||||
// Setup input actions
|
||||
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
||||
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
||||
}
|
||||
|
||||
private void OnUndoRedo(IUndoAction action)
|
||||
{
|
||||
MarkAsEdited();
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
private void OnScriptsReloadBegin()
|
||||
@@ -64,13 +85,14 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
|
||||
ClearEditedFlag();
|
||||
_item.RefreshThumbnail();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateToolstrip()
|
||||
{
|
||||
_saveButton.Enabled = IsEdited;
|
||||
_undoButton.Enabled = _undo.CanUndo;
|
||||
_redoButton.Enabled = _undo.CanRedo;
|
||||
|
||||
base.UpdateToolstrip();
|
||||
}
|
||||
@@ -80,6 +102,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
_object = Asset.CreateInstance();
|
||||
_presenter.Select(_object);
|
||||
_undo.Clear();
|
||||
ClearEditedFlag();
|
||||
|
||||
// Auto-close on scripting reload if json asset is from game scripts (it might be reloaded)
|
||||
|
||||
212
Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs
Normal file
212
Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
/// <summary>
|
||||
/// Editor window to view/modify <see cref="LocalizedStringTable"/> asset.
|
||||
/// </summary>
|
||||
/// <seealso cref="LocalizedStringTable" />
|
||||
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
||||
public sealed class LocalizedStringTableWindow : AssetEditorWindowBase<LocalizedStringTable>
|
||||
{
|
||||
private readonly CustomEditorPresenter _presenter;
|
||||
private readonly ToolStripButton _saveButton;
|
||||
private readonly ToolStripButton _undoButton;
|
||||
private readonly ToolStripButton _redoButton;
|
||||
private readonly Undo _undo;
|
||||
private Proxy _proxy;
|
||||
|
||||
private class EntryEditor : CustomEditor
|
||||
{
|
||||
private TextBox[] _textBoxes;
|
||||
private bool _isRefreshing;
|
||||
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var values = (string[])Values[0];
|
||||
if (values == null || values.Length == 0)
|
||||
{
|
||||
values = new string[1];
|
||||
values[0] = string.Empty;
|
||||
}
|
||||
if (_textBoxes == null || _textBoxes.Length != values.Length)
|
||||
_textBoxes = new TextBox[values.Length];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
var value = values[i];
|
||||
var textBox = layout.TextBox(value.IsMultiline());
|
||||
textBox.TextBox.Tag = i;
|
||||
textBox.TextBox.Text = value;
|
||||
textBox.TextBox.TextBoxEditEnd += OnEditEnd;
|
||||
_textBoxes[i] = textBox.TextBox;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
var values = (string[])Values[0];
|
||||
if (values != null && values.Length == _textBoxes.Length)
|
||||
{
|
||||
_isRefreshing = true;
|
||||
var style = FlaxEngine.GUI.Style.Current;
|
||||
var wrongColor = new Color(1.0f, 0.0f, 0.02745f, 1.0f);
|
||||
var wrongColorBorder = Color.Lerp(wrongColor, style.TextBoxBackground, 0.6f);
|
||||
for (int i = 0; i < _textBoxes.Length; i++)
|
||||
{
|
||||
var textBox = _textBoxes[i];
|
||||
if (!textBox.IsEditing)
|
||||
{
|
||||
textBox.Text = values[i];
|
||||
if (string.IsNullOrEmpty(textBox.Text))
|
||||
{
|
||||
textBox.BorderColor = wrongColorBorder;
|
||||
textBox.BorderSelectedColor = wrongColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
textBox.BorderColor = Color.Transparent;
|
||||
textBox.BorderSelectedColor = style.BackgroundSelected;
|
||||
}
|
||||
}
|
||||
}
|
||||
_isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
base.Deinitialize();
|
||||
|
||||
_textBoxes = null;
|
||||
_isRefreshing = false;
|
||||
}
|
||||
|
||||
private void OnEditEnd(TextBoxBase textBox)
|
||||
{
|
||||
if (_isRefreshing)
|
||||
return;
|
||||
var values = (string[])Values[0];
|
||||
var length = Mathf.Max(values?.Length ?? 0, 1);
|
||||
var toSet = new string[length];
|
||||
if (values != null && values.Length == length)
|
||||
Array.Copy(values, toSet, length);
|
||||
var index = (int)textBox.Tag;
|
||||
toSet[index] = textBox.Text;
|
||||
SetValue(toSet);
|
||||
}
|
||||
}
|
||||
|
||||
private class Proxy
|
||||
{
|
||||
[EditorOrder(0), EditorDisplay("General"), Tooltip("The locale of the localized string table (eg. pl-PL)."),]
|
||||
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.CultureInfoEditor))]
|
||||
public string Locale;
|
||||
|
||||
[EditorOrder(10), EditorDisplay("Entries", EditorDisplayAttribute.InlineStyle), Tooltip("The string table. Maps the message id into the localized text. For plural messages the list contains separate items for value numbers.")]
|
||||
[Collection(Spacing = 10, OverrideEditorTypeName = "FlaxEditor.Windows.Assets.LocalizedStringTableWindow+EntryEditor")]
|
||||
public Dictionary<string, string[]> Entries;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public LocalizedStringTableWindow(Editor editor, AssetItem item)
|
||||
: base(editor, item)
|
||||
{
|
||||
// Undo
|
||||
_undo = new Undo();
|
||||
_undo.UndoDone += OnUndoRedo;
|
||||
_undo.RedoDone += OnUndoRedo;
|
||||
|
||||
// Toolstrip
|
||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Save32, Save).LinkTooltip("Save");
|
||||
_toolstrip.AddSeparator();
|
||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo32, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo32, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||
|
||||
// Panel
|
||||
var panel = new Panel(ScrollBars.Vertical)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = new Margin(0, 0, _toolstrip.Bottom, 0),
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// Properties
|
||||
_presenter = new CustomEditorPresenter(_undo, "Loading...");
|
||||
_presenter.Panel.Parent = panel;
|
||||
_presenter.Modified += MarkAsEdited;
|
||||
|
||||
// Setup input actions
|
||||
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
||||
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
||||
}
|
||||
|
||||
private void OnUndoRedo(IUndoAction action)
|
||||
{
|
||||
MarkAsEdited();
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Save()
|
||||
{
|
||||
if (!IsEdited)
|
||||
return;
|
||||
|
||||
_asset.Locale = _proxy.Locale;
|
||||
_asset.Entries = _proxy.Entries;
|
||||
if (_asset.Save(_item.Path))
|
||||
{
|
||||
Editor.LogError("Cannot save asset.");
|
||||
return;
|
||||
}
|
||||
|
||||
ClearEditedFlag();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateToolstrip()
|
||||
{
|
||||
_saveButton.Enabled = IsEdited;
|
||||
_undoButton.Enabled = _undo.CanUndo;
|
||||
_redoButton.Enabled = _undo.CanRedo;
|
||||
|
||||
base.UpdateToolstrip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAssetLoaded()
|
||||
{
|
||||
_proxy = new Proxy
|
||||
{
|
||||
Locale = _asset.Locale,
|
||||
Entries = _asset.Entries,
|
||||
};
|
||||
_presenter.Select(_proxy);
|
||||
_undo.Clear();
|
||||
ClearEditedFlag();
|
||||
|
||||
base.OnAssetLoaded();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnItemReimported(ContentItem item)
|
||||
{
|
||||
// Refresh the properties (will get new data in OnAssetLoaded)
|
||||
_presenter.Deselect();
|
||||
ClearEditedFlag();
|
||||
|
||||
base.OnItemReimported(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,7 +565,6 @@ namespace FlaxEditor.Windows.Assets
|
||||
public UVsLayoutPreviewControl()
|
||||
{
|
||||
Offsets = new Margin(4);
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchMiddle;
|
||||
AutomaticInvalidate = false;
|
||||
}
|
||||
|
||||
@@ -619,9 +618,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DrawChildren()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
base.DrawChildren();
|
||||
base.DrawSelf();
|
||||
|
||||
var size = Size;
|
||||
if (_channel == UVChannel.None || size.MaxValue < 5.0f)
|
||||
|
||||
@@ -114,9 +114,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
base.Draw();
|
||||
base.DrawSelf();
|
||||
|
||||
var style = Style.Current;
|
||||
var asset = _window.Asset;
|
||||
@@ -676,7 +676,6 @@ namespace FlaxEditor.Windows.Assets
|
||||
public UVsLayoutPreviewControl()
|
||||
{
|
||||
Offsets = new Margin(4);
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchMiddle;
|
||||
AutomaticInvalidate = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace FlaxEditor.Windows
|
||||
{ PlatformType.PS4, new PS4() },
|
||||
{ PlatformType.XboxScarlett, new XboxScarlett() },
|
||||
{ PlatformType.Android, new Android() },
|
||||
{ PlatformType.Switch, new Switch() },
|
||||
};
|
||||
|
||||
public BuildTabProxy(GameCookerWindow win, PlatformSelector platformSelector)
|
||||
@@ -57,6 +58,7 @@ namespace FlaxEditor.Windows
|
||||
PerPlatformOptions[PlatformType.PS4].Init("Output/PS4", "PS4");
|
||||
PerPlatformOptions[PlatformType.XboxScarlett].Init("Output/XboxScarlett", "XboxScarlett");
|
||||
PerPlatformOptions[PlatformType.Android].Init("Output/Android", "Android");
|
||||
PerPlatformOptions[PlatformType.Switch].Init("Output/Switch", "Switch");
|
||||
}
|
||||
|
||||
public abstract class Platform
|
||||
@@ -188,6 +190,11 @@ namespace FlaxEditor.Windows
|
||||
protected override BuildPlatform BuildPlatform => BuildPlatform.AndroidARM64;
|
||||
}
|
||||
|
||||
public class Switch : Platform
|
||||
{
|
||||
protected override BuildPlatform BuildPlatform => BuildPlatform.Switch;
|
||||
}
|
||||
|
||||
public class Editor : CustomEditor
|
||||
{
|
||||
private PlatformType _platform;
|
||||
@@ -229,6 +236,9 @@ namespace FlaxEditor.Windows
|
||||
case PlatformType.Android:
|
||||
name = "Android";
|
||||
break;
|
||||
case PlatformType.Switch:
|
||||
name = "Switch";
|
||||
break;
|
||||
default:
|
||||
name = CustomEditorsUtil.GetPropertyNameUI(_platform.ToString());
|
||||
break;
|
||||
|
||||
@@ -120,6 +120,17 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("All we had to do was follow the damn train, CJ"),
|
||||
TEXT("28 stab wounds"),
|
||||
TEXT("Here we go again"),
|
||||
TEXT("@everyone"),
|
||||
TEXT("uwu some spiders on the ceiling"),
|
||||
TEXT("There you are you little shit"),
|
||||
TEXT("potato"),
|
||||
TEXT("python is a programming snek"),
|
||||
TEXT("Flax will start when pigs will fly"),
|
||||
TEXT("I'm the android sent by CyberLife"),
|
||||
TEXT("fancy-ass ray tracing rtx on lighting"),
|
||||
TEXT("ZOINKS"),
|
||||
TEXT("Scooby dooby doo"),
|
||||
TEXT("You shall not load"),
|
||||
};
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Curve.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Animations/Curve.h"
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -636,7 +636,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
transform.Scale = (Vector3)tryGetValue(node->GetBox(4), Vector3::One);
|
||||
|
||||
// Skip if no change will be performed
|
||||
if (boneIndex < 0 || boneIndex >= _skeletonBonesCount || transformMode == BoneTransformMode::None || transform.IsIdentity())
|
||||
if (boneIndex < 0 || boneIndex >= _skeletonBonesCount || transformMode == BoneTransformMode::None || (transformMode == BoneTransformMode::Add && transform.IsIdentity()))
|
||||
{
|
||||
// Pass through the input
|
||||
value = Value::Null;
|
||||
@@ -1628,7 +1628,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
transform.Scale = (Vector3)tryGetValue(node->GetBox(4), Vector3::One);
|
||||
|
||||
// Skip if no change will be performed
|
||||
if (nodeIndex < 0 || nodeIndex >= _skeletonNodesCount || transformMode == BoneTransformMode::None || transform.IsIdentity())
|
||||
if (nodeIndex < 0 || nodeIndex >= _skeletonNodesCount || transformMode == BoneTransformMode::None || (transformMode == BoneTransformMode::Add && transform.IsIdentity()))
|
||||
{
|
||||
// Pass through the input
|
||||
value = Value::Null;
|
||||
|
||||
@@ -46,6 +46,10 @@ public class Audio : EngineModule
|
||||
case TargetPlatform.Android:
|
||||
useOpenAL = true;
|
||||
break;
|
||||
case TargetPlatform.Switch:
|
||||
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "Audio"));
|
||||
options.CompileEnv.PreprocessorDefinitions.Add("AUDIO_API_SWITCH");
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
|
||||
namespace CSG
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -225,7 +225,7 @@ bool Asset::WaitForLoaded(double timeoutInMilliseconds)
|
||||
while (!Engine::ShouldExit())
|
||||
{
|
||||
// Try to execute content tasks
|
||||
while (task->IsQueued())
|
||||
while (task->IsQueued() && !Engine::ShouldExit())
|
||||
{
|
||||
// Pick this task from the queue
|
||||
ContentLoadTask* tmp;
|
||||
|
||||
@@ -274,3 +274,9 @@ public:
|
||||
OnSet(asset);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
uint32 GetHash(const AssetReference<T>& key)
|
||||
{
|
||||
return GetHash(key.GetID());
|
||||
}
|
||||
|
||||
@@ -396,21 +396,21 @@ bool Model::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
auto& meshData = meshesData[meshIndex];
|
||||
|
||||
// Vertex Buffer 0 (required)
|
||||
auto task = mesh.ExtractDataAsync(MeshBufferType::Vertex0, meshData.VB0);
|
||||
auto task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex0, meshData.VB0);
|
||||
if (task == nullptr)
|
||||
return true;
|
||||
task->Start();
|
||||
tasks.Add(task);
|
||||
|
||||
// Vertex Buffer 1 (required)
|
||||
task = mesh.ExtractDataAsync(MeshBufferType::Vertex1, meshData.VB1);
|
||||
task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex1, meshData.VB1);
|
||||
if (task == nullptr)
|
||||
return true;
|
||||
task->Start();
|
||||
tasks.Add(task);
|
||||
|
||||
// Vertex Buffer 2 (optional)
|
||||
task = mesh.ExtractDataAsync(MeshBufferType::Vertex2, meshData.VB2);
|
||||
task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex2, meshData.VB2);
|
||||
if (task)
|
||||
{
|
||||
task->Start();
|
||||
@@ -418,7 +418,7 @@ bool Model::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
}
|
||||
|
||||
// Index Buffer (required)
|
||||
task = mesh.ExtractDataAsync(MeshBufferType::Index, meshData.IB);
|
||||
task = mesh.DownloadDataGPUAsync(MeshBufferType::Index, meshData.IB);
|
||||
if (task == nullptr)
|
||||
return true;
|
||||
task->Start();
|
||||
@@ -618,6 +618,19 @@ void Model::SetupMaterialSlots(int32 slotsCount)
|
||||
}
|
||||
}
|
||||
|
||||
int32 Model::GetLODsCount() const
|
||||
{
|
||||
return LODs.Count();
|
||||
}
|
||||
|
||||
void Model::GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
meshes.Resize(lod.Meshes.Count());
|
||||
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
|
||||
meshes[meshIndex] = &lod.Meshes[meshIndex];
|
||||
}
|
||||
|
||||
void Model::InitAsVirtual()
|
||||
{
|
||||
// Init with a single LOD and one mesh
|
||||
@@ -628,6 +641,21 @@ void Model::InitAsVirtual()
|
||||
BinaryAsset::InitAsVirtual();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void Model::GetReferences(Array<Guid>& output) const
|
||||
{
|
||||
// Base
|
||||
BinaryAsset::GetReferences(output);
|
||||
|
||||
for (int32 i = 0; i < MaterialSlots.Count(); i++)
|
||||
{
|
||||
output.Add(MaterialSlots[i].Material.GetID());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int32 Model::GetMaxResidency() const
|
||||
{
|
||||
return LODs.Count();
|
||||
@@ -837,3 +865,33 @@ AssetChunksFlag Model::getChunksToPreload() const
|
||||
// Note: we don't preload any LODs here because it's done by the Streaming Manager
|
||||
return GET_CHUNK_FLAG(0);
|
||||
}
|
||||
|
||||
void ModelBase::SetupMaterialSlots(int32 slotsCount)
|
||||
{
|
||||
CHECK(slotsCount >= 0 && slotsCount < 4096);
|
||||
if (!IsVirtual() && WaitForLoaded())
|
||||
return;
|
||||
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
const int32 prevCount = MaterialSlots.Count();
|
||||
MaterialSlots.Resize(slotsCount, false);
|
||||
|
||||
// Initialize slot names
|
||||
for (int32 i = prevCount; i < slotsCount; i++)
|
||||
MaterialSlots[i].Name = String::Format(TEXT("Material {0}"), i + 1);
|
||||
}
|
||||
|
||||
MaterialSlot* ModelBase::GetSlot(const StringView& name)
|
||||
{
|
||||
MaterialSlot* result = nullptr;
|
||||
for (auto& slot : MaterialSlots)
|
||||
{
|
||||
if (slot.Name == name)
|
||||
{
|
||||
result = &slot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -55,15 +55,6 @@ public:
|
||||
return LODs.HasItems();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets amount of the level of details in the model
|
||||
/// </summary>
|
||||
/// <returns>Amount of the level of details in the model</returns>
|
||||
FORCE_INLINE int32 GetLODsCount() const
|
||||
{
|
||||
return LODs.Count();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of loaded model LODs.
|
||||
/// </summary>
|
||||
@@ -237,18 +228,11 @@ public:
|
||||
|
||||
// [ModelBase]
|
||||
void SetupMaterialSlots(int32 slotsCount) override;
|
||||
int32 GetLODsCount() const override;
|
||||
void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) override;
|
||||
void InitAsVirtual() override;
|
||||
#if USE_EDITOR
|
||||
void GetReferences(Array<Guid>& output) const override
|
||||
{
|
||||
// Base
|
||||
BinaryAsset::GetReferences(output);
|
||||
|
||||
for (int32 i = 0; i < MaterialSlots.Count(); i++)
|
||||
{
|
||||
output.Add(MaterialSlots[i].Material.GetID());
|
||||
}
|
||||
}
|
||||
void GetReferences(Array<Guid>& output) const override;
|
||||
#endif
|
||||
|
||||
// [StreamableResource]
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "Engine/Graphics/Models/MaterialSlot.h"
|
||||
#include "Engine/Streaming/StreamableResource.h"
|
||||
|
||||
class MeshBase;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for asset types that can contain a model resource.
|
||||
/// </summary>
|
||||
@@ -44,38 +46,23 @@ public:
|
||||
/// <summary>
|
||||
/// Resizes the material slots collection. Updates meshes that were using removed slots.
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void SetupMaterialSlots(int32 slotsCount)
|
||||
{
|
||||
CHECK(slotsCount >= 0 && slotsCount < 4096);
|
||||
if (!IsVirtual() && WaitForLoaded())
|
||||
return;
|
||||
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
const int32 prevCount = MaterialSlots.Count();
|
||||
MaterialSlots.Resize(slotsCount, false);
|
||||
|
||||
// Initialize slot names
|
||||
for (int32 i = prevCount; i < slotsCount; i++)
|
||||
MaterialSlots[i].Name = String::Format(TEXT("Material {0}"), i + 1);
|
||||
}
|
||||
API_FUNCTION() virtual void SetupMaterialSlots(int32 slotsCount);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the material slot by the name.
|
||||
/// </summary>
|
||||
/// <param name="name">The slot name.</param>
|
||||
/// <returns>The material slot with the given name or null if cannot find it (asset may be not loaded yet).</returns>
|
||||
API_FUNCTION() MaterialSlot* GetSlot(const StringView& name)
|
||||
{
|
||||
MaterialSlot* result = nullptr;
|
||||
for (auto& slot : MaterialSlots)
|
||||
{
|
||||
if (slot.Name == name)
|
||||
{
|
||||
result = &slot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
API_FUNCTION() MaterialSlot* GetSlot(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets amount of the level of details in the model.
|
||||
/// </summary>
|
||||
/// <returns>Amount of the level of details in the model.</returns>
|
||||
virtual int32 GetLODsCount() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the meshes for a particular LOD index.
|
||||
/// </summary>
|
||||
virtual void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) = 0;
|
||||
};
|
||||
|
||||
@@ -137,6 +137,18 @@ Array<String> SkinnedModel::GetBlendShapes()
|
||||
return result;
|
||||
}
|
||||
|
||||
ContentLoadTask* SkinnedModel::RequestLODDataAsync(int32 lodIndex)
|
||||
{
|
||||
const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
|
||||
return RequestChunkDataAsync(chunkIndex);
|
||||
}
|
||||
|
||||
void SkinnedModel::GetLODData(int32 lodIndex, BytesContainer& data) const
|
||||
{
|
||||
const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
|
||||
GetChunkData(chunkIndex, data);
|
||||
}
|
||||
|
||||
bool SkinnedModel::Intersects(const Ray& ray, const Matrix& world, float& distance, Vector3& normal, SkinnedMesh** mesh, int32 lodIndex)
|
||||
{
|
||||
return LODs[lodIndex].Intersects(ray, world, distance, normal, mesh);
|
||||
@@ -389,7 +401,7 @@ bool SkinnedModel::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
{
|
||||
auto& slot = MaterialSlots[materialSlotIndex];
|
||||
|
||||
const auto id =slot.Material.GetID();
|
||||
const auto id = slot.Material.GetID();
|
||||
stream->Write(&id);
|
||||
stream->WriteByte(static_cast<byte>(slot.ShadowsMode));
|
||||
stream->WriteString(slot.Name, 11);
|
||||
@@ -505,14 +517,14 @@ bool SkinnedModel::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
auto& meshData = meshesData[meshIndex];
|
||||
|
||||
// Vertex Buffer 0 (required)
|
||||
auto task = mesh.DownloadDataAsyncGPU(MeshBufferType::Vertex0, meshData.VB0);
|
||||
auto task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex0, meshData.VB0);
|
||||
if (task == nullptr)
|
||||
return true;
|
||||
task->Start();
|
||||
tasks.Add(task);
|
||||
|
||||
// Index Buffer (required)
|
||||
task = mesh.DownloadDataAsyncGPU(MeshBufferType::Index, meshData.IB);
|
||||
task = mesh.DownloadDataGPUAsync(MeshBufferType::Index, meshData.IB);
|
||||
if (task == nullptr)
|
||||
return true;
|
||||
task->Start();
|
||||
@@ -701,6 +713,19 @@ void SkinnedModel::SetupMaterialSlots(int32 slotsCount)
|
||||
}
|
||||
}
|
||||
|
||||
int32 SkinnedModel::GetLODsCount() const
|
||||
{
|
||||
return LODs.Count();
|
||||
}
|
||||
|
||||
void SkinnedModel::GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
meshes.Resize(lod.Meshes.Count());
|
||||
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
|
||||
meshes[meshIndex] = &lod.Meshes[meshIndex];
|
||||
}
|
||||
|
||||
void SkinnedModel::InitAsVirtual()
|
||||
{
|
||||
// Init with one mesh and single bone
|
||||
@@ -723,6 +748,21 @@ void SkinnedModel::InitAsVirtual()
|
||||
BinaryAsset::InitAsVirtual();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void SkinnedModel::GetReferences(Array<Guid>& output) const
|
||||
{
|
||||
// Base
|
||||
BinaryAsset::GetReferences(output);
|
||||
|
||||
for (int32 i = 0; i < MaterialSlots.Count(); i++)
|
||||
{
|
||||
output.Add(MaterialSlots[i].Material.GetID());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int32 SkinnedModel::GetMaxResidency() const
|
||||
{
|
||||
return LODs.Count();
|
||||
|
||||
@@ -61,15 +61,6 @@ public:
|
||||
return LODs.HasItems();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets amount of the level of details in the model
|
||||
/// </summary>
|
||||
/// <returns>Amount of the level of details in the model</returns>
|
||||
FORCE_INLINE int32 GetLODsCount() const
|
||||
{
|
||||
return LODs.Count();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of loaded model LODs.
|
||||
/// </summary>
|
||||
@@ -184,22 +175,14 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="lodIndex">Index of the LOD.</param>
|
||||
/// <returns>Task that will gather chunk data or null if already here.</returns>
|
||||
ContentLoadTask* RequestLODDataAsync(int32 lodIndex)
|
||||
{
|
||||
const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
|
||||
return RequestChunkDataAsync(chunkIndex);
|
||||
}
|
||||
ContentLoadTask* RequestLODDataAsync(int32 lodIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model LOD data (links bytes).
|
||||
/// </summary>
|
||||
/// <param name="lodIndex">Index of the LOD.</param>
|
||||
/// <param name="data">The data (may be missing if failed to get it).</param>
|
||||
void GetLODData(int32 lodIndex, BytesContainer& data) const
|
||||
{
|
||||
const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
|
||||
GetChunkData(chunkIndex, data);
|
||||
}
|
||||
void GetLODData(int32 lodIndex, BytesContainer& data) const;
|
||||
|
||||
public:
|
||||
|
||||
@@ -300,18 +283,11 @@ public:
|
||||
|
||||
// [ModelBase]
|
||||
void SetupMaterialSlots(int32 slotsCount) override;
|
||||
int32 GetLODsCount() const override;
|
||||
void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) override;
|
||||
void InitAsVirtual() override;
|
||||
#if USE_EDITOR
|
||||
void GetReferences(Array<Guid>& output) const override
|
||||
{
|
||||
// Base
|
||||
BinaryAsset::GetReferences(output);
|
||||
|
||||
for (int32 i = 0; i < MaterialSlots.Count(); i++)
|
||||
{
|
||||
output.Add(MaterialSlots[i].Material.GetID());
|
||||
}
|
||||
}
|
||||
void GetReferences(Array<Guid>& output) const override;
|
||||
#endif
|
||||
|
||||
// [StreamableResource]
|
||||
|
||||
@@ -405,38 +405,42 @@ void AssetsCache::RegisterAssets(FlaxStorage* storage)
|
||||
void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const StringView& path)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
ScopeLock lock(_locker);
|
||||
|
||||
// Mark registry as draft
|
||||
_isDirty = true;
|
||||
|
||||
// Check if asset has been already added to the registry
|
||||
bool isMissing = true;
|
||||
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto& e = i->Value;
|
||||
|
||||
// Compare IDs
|
||||
if (e.Info.ID == id)
|
||||
{
|
||||
// Update registry entry
|
||||
e.Info.Path = path;
|
||||
e.Info.TypeName = typeName;
|
||||
|
||||
// Back
|
||||
if (e.Info.Path != path)
|
||||
{
|
||||
e.Info.Path = path;
|
||||
_isDirty = true;
|
||||
}
|
||||
if (e.Info.TypeName != typeName)
|
||||
{
|
||||
e.Info.TypeName = typeName;
|
||||
_isDirty = true;
|
||||
}
|
||||
isMissing = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Compare paths
|
||||
if (e.Info.Path == path)
|
||||
{
|
||||
// Update registry entry
|
||||
e.Info.ID = id;
|
||||
e.Info.TypeName = typeName;
|
||||
|
||||
// Back
|
||||
if (e.Info.ID != id)
|
||||
{
|
||||
e.Info.Path = path;
|
||||
_isDirty = true;
|
||||
}
|
||||
if (e.Info.TypeName != typeName)
|
||||
{
|
||||
e.Info.TypeName = typeName;
|
||||
_isDirty = true;
|
||||
}
|
||||
isMissing = false;
|
||||
break;
|
||||
}
|
||||
@@ -445,9 +449,8 @@ void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const St
|
||||
if (isMissing)
|
||||
{
|
||||
LOG(Info, "Register asset {0}:{1} \'{2}\'", id, typeName, path);
|
||||
|
||||
// Add new asset entry
|
||||
_registry.Add(id, Entry(id, typeName, path));
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class FlaxStorage;
|
||||
/// The binary assets factory base class.
|
||||
/// </summary>
|
||||
/// <seealso cref="IAssetFactory" />
|
||||
class BinaryAssetFactoryBase : public IAssetFactory
|
||||
class FLAXENGINE_API BinaryAssetFactoryBase : public IAssetFactory
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class IAssetUpgrader;
|
||||
/// <summary>
|
||||
/// The asset objects factory.
|
||||
/// </summary>
|
||||
class IAssetFactory
|
||||
class FLAXENGINE_API IAssetFactory
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/// The Json assets factory base class.
|
||||
/// </summary>
|
||||
/// <seealso cref="IAssetFactory" />
|
||||
class JsonAssetFactoryBase : public IAssetFactory
|
||||
class FLAXENGINE_API JsonAssetFactoryBase : public IAssetFactory
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -23,7 +23,6 @@ public:
|
||||
{
|
||||
return Create(info);
|
||||
}
|
||||
|
||||
Asset* NewVirtual(const AssetInfo& info) override
|
||||
{
|
||||
return Create(info);
|
||||
@@ -47,12 +46,13 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
#define REGISTER_JSON_ASSET(type, typeName) \
|
||||
#define REGISTER_JSON_ASSET(type, typeName, supportsVirtualAssets) \
|
||||
const String type::TypeName = TEXT(typeName); \
|
||||
class CONCAT_MACROS(Factory, type) : public JsonAssetFactory<type> \
|
||||
{ \
|
||||
public: \
|
||||
CONCAT_MACROS(Factory, type)() { IAssetFactory::Get().Add(type::TypeName, this); } \
|
||||
~CONCAT_MACROS(Factory, type)() { IAssetFactory::Get().Remove(type::TypeName); } \
|
||||
bool SupportsVirtualAssets() const override { return supportsVirtualAssets; } \
|
||||
}; \
|
||||
static CONCAT_MACROS(Factory, type) CONCAT_MACROS(CFactory, type)
|
||||
|
||||
@@ -176,7 +176,7 @@ void JsonAssetBase::onRename(const StringView& newPath)
|
||||
|
||||
#endif
|
||||
|
||||
REGISTER_JSON_ASSET(JsonAsset, "FlaxEngine.JsonAsset");
|
||||
REGISTER_JSON_ASSET(JsonAsset, "FlaxEngine.JsonAsset", true);
|
||||
|
||||
JsonAsset::JsonAsset(const SpawnParams& params, const AssetInfo* info)
|
||||
: JsonAssetBase(params, info)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Threading/Task.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
|
||||
class Asset;
|
||||
class LoadingThread;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/// <summary>
|
||||
/// Binary asset upgrading context structure.
|
||||
/// </summary>
|
||||
struct AssetMigrationContext
|
||||
struct FLAXENGINE_API AssetMigrationContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The input data.
|
||||
@@ -63,7 +63,7 @@ typedef bool (*UpgradeHandler)(AssetMigrationContext& context);
|
||||
/// Binary Assets Upgrader base class
|
||||
/// </summary>
|
||||
/// <seealso cref="IAssetUpgrader" />
|
||||
class BinaryAssetUpgrader : public IAssetUpgrader
|
||||
class FLAXENGINE_API BinaryAssetUpgrader : public IAssetUpgrader
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/// <summary>
|
||||
/// The assets upgrading objects interface.
|
||||
/// </summary>
|
||||
class IAssetUpgrader
|
||||
class FLAXENGINE_API IAssetUpgrader
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Graphics/Models/ModelData.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
|
||||
/// <summary>
|
||||
/// Model Asset Upgrader
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "BinaryAssetUpgrader.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Graphics/Shaders/Cache/ShaderStorage.h"
|
||||
|
||||
/// <summary>
|
||||
/// Material Asset and Shader Asset Upgrader
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Graphics/Models/Types.h"
|
||||
#include "Engine/Core/Math/BoundingBox.h"
|
||||
#include "Engine/Core/Math/BoundingSphere.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
|
||||
/// <summary>
|
||||
/// Skinned Model Asset Upgrader
|
||||
|
||||
@@ -229,3 +229,9 @@ public:
|
||||
OnSet(asset);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
uint32 GetHash(const WeakAssetReference<T>& key)
|
||||
{
|
||||
return GetHash(key.GetID());
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "CreateParticleEmitterFunction.h"
|
||||
#include "CreateAnimationGraphFunction.h"
|
||||
#include "CreateVisualScript.h"
|
||||
#include "CreateJson.h"
|
||||
|
||||
// Tags used to detect asset creation mode
|
||||
const String AssetsImportingManager::CreateTextureTag(TEXT("Texture"));
|
||||
@@ -91,6 +92,10 @@ CreateAssetResult CreateAssetContext::Run(const CreateAssetFunction& callback)
|
||||
if (result != CreateAssetResult::Ok)
|
||||
return result;
|
||||
|
||||
// Skip for non-flax assets (eg. json resource or custom asset type)
|
||||
if (!TargetAssetPath.EndsWith(ASSET_FILES_EXTENSION))
|
||||
return CreateAssetResult::Ok;
|
||||
|
||||
// Validate assigned TypeID
|
||||
if (Data.Header.TypeName.IsEmpty())
|
||||
{
|
||||
@@ -112,8 +117,7 @@ CreateAssetResult CreateAssetContext::Run(const CreateAssetFunction& callback)
|
||||
}
|
||||
|
||||
// Save file
|
||||
result = Save();
|
||||
|
||||
result = FlaxStorage::Create(OutputPath, Data) ? CreateAssetResult::CannotSaveFile : CreateAssetResult::Ok;
|
||||
if (result == CreateAssetResult::Ok)
|
||||
{
|
||||
_applyChangesResult = CreateAssetResult::Abort;
|
||||
@@ -161,11 +165,6 @@ void CreateAssetContext::AddMeta(JsonWriter& writer) const
|
||||
writer.String(Platform::GetUserName());
|
||||
}
|
||||
|
||||
CreateAssetResult CreateAssetContext::Save()
|
||||
{
|
||||
return FlaxStorage::Create(OutputPath, Data) ? CreateAssetResult::CannotSaveFile : CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
void CreateAssetContext::ApplyChanges()
|
||||
{
|
||||
// Get access
|
||||
@@ -231,8 +230,6 @@ bool AssetsImportingManager::Create(const String& tag, const StringView& outputP
|
||||
|
||||
bool AssetsImportingManager::Import(const StringView& inputPath, const StringView& outputPath, Guid& assetId, void* arg)
|
||||
{
|
||||
ASSERT(outputPath.EndsWith(StringView(ASSET_FILES_EXTENSION)));
|
||||
|
||||
LOG(Info, "Importing file '{0}' to '{1}'...", inputPath, outputPath);
|
||||
|
||||
// Check if input file exists
|
||||
@@ -246,8 +243,7 @@ bool AssetsImportingManager::Import(const StringView& inputPath, const StringVie
|
||||
const String extension = FileSystem::GetExtension(inputPath).ToLower();
|
||||
|
||||
// Special case for raw assets
|
||||
const String assetExtension = ASSET_FILES_EXTENSION;
|
||||
if (assetExtension.Compare(extension, StringSearchCase::IgnoreCase) == 0)
|
||||
if (StringView(ASSET_FILES_EXTENSION).Compare(StringView(extension), StringSearchCase::IgnoreCase) == 0)
|
||||
{
|
||||
// Simply copy file (content layer will resolve duplicated IDs, etc.)
|
||||
return FileSystem::CopyFile(outputPath, inputPath);
|
||||
@@ -266,8 +262,6 @@ bool AssetsImportingManager::Import(const StringView& inputPath, const StringVie
|
||||
|
||||
bool AssetsImportingManager::ImportIfEdited(const StringView& inputPath, const StringView& outputPath, Guid& assetId, void* arg)
|
||||
{
|
||||
ASSERT(outputPath.EndsWith(StringView(ASSET_FILES_EXTENSION)));
|
||||
|
||||
// Check if asset not exists
|
||||
if (!FileSystem::FileExists(outputPath))
|
||||
{
|
||||
@@ -383,64 +377,67 @@ bool AssetsImportingManagerService::Init()
|
||||
AssetImporter InBuildImporters[] =
|
||||
{
|
||||
// Textures and Cube Textures
|
||||
{ TEXT("tga"), ImportTexture::Import },
|
||||
{ TEXT("dds"), ImportTexture::Import },
|
||||
{ TEXT("png"), ImportTexture::Import },
|
||||
{ TEXT("bmp"), ImportTexture::Import },
|
||||
{ TEXT("gif"), ImportTexture::Import },
|
||||
{ TEXT("tiff"), ImportTexture::Import },
|
||||
{ TEXT("tif"), ImportTexture::Import },
|
||||
{ TEXT("jpeg"), ImportTexture::Import },
|
||||
{ TEXT("jpg"), ImportTexture::Import },
|
||||
{ TEXT("hdr"), ImportTexture::Import },
|
||||
{ TEXT("raw"), ImportTexture::Import },
|
||||
{ TEXT("tga"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("dds"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("png"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("bmp"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("gif"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("tiff"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("tif"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("jpeg"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("jpg"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("hdr"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
{ TEXT("raw"), ASSET_FILES_EXTENSION, ImportTexture::Import },
|
||||
|
||||
// IES Profiles
|
||||
{ TEXT("ies"), ImportTexture::ImportIES },
|
||||
{ TEXT("ies"), ASSET_FILES_EXTENSION, ImportTexture::ImportIES },
|
||||
|
||||
// Shaders
|
||||
{ TEXT("shader"), ImportShader::Import },
|
||||
{ TEXT("shader"), ASSET_FILES_EXTENSION, ImportShader::Import },
|
||||
|
||||
// Audio
|
||||
{ TEXT("wav"), ImportAudio::ImportWav },
|
||||
{ TEXT("mp3"), ImportAudio::ImportMp3 },
|
||||
{ TEXT("wav"), ASSET_FILES_EXTENSION, ImportAudio::ImportWav },
|
||||
{ TEXT("mp3"), ASSET_FILES_EXTENSION, ImportAudio::ImportMp3 },
|
||||
#if COMPILE_WITH_OGG_VORBIS
|
||||
{ TEXT("ogg"), ImportAudio::ImportOgg },
|
||||
{ TEXT("ogg"), ASSET_FILES_EXTENSION, ImportAudio::ImportOgg },
|
||||
#endif
|
||||
|
||||
// Fonts
|
||||
{ TEXT("ttf"), ImportFont::Import },
|
||||
{ TEXT("otf"), ImportFont::Import },
|
||||
{ TEXT("ttf"), ASSET_FILES_EXTENSION, ImportFont::Import },
|
||||
{ TEXT("otf"), ASSET_FILES_EXTENSION, ImportFont::Import },
|
||||
|
||||
// Models
|
||||
{ TEXT("obj"), ImportModelFile::Import },
|
||||
{ TEXT("fbx"), ImportModelFile::Import },
|
||||
{ TEXT("x"), ImportModelFile::Import },
|
||||
{ TEXT("dae"), ImportModelFile::Import },
|
||||
{ TEXT("gltf"), ImportModelFile::Import },
|
||||
{ TEXT("glb"), ImportModelFile::Import },
|
||||
{ TEXT("obj"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("fbx"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("x"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("dae"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("gltf"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("glb"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
|
||||
// gettext PO files
|
||||
{ TEXT("po"), TEXT("json"), CreateJson::ImportPo },
|
||||
|
||||
// Models (untested formats - may fail :/)
|
||||
{ TEXT("blend"), ImportModelFile::Import },
|
||||
{ TEXT("bvh"), ImportModelFile::Import },
|
||||
{ TEXT("ase"), ImportModelFile::Import },
|
||||
{ TEXT("ply"), ImportModelFile::Import },
|
||||
{ TEXT("dxf"), ImportModelFile::Import },
|
||||
{ TEXT("ifc"), ImportModelFile::Import },
|
||||
{ TEXT("nff"), ImportModelFile::Import },
|
||||
{ TEXT("smd"), ImportModelFile::Import },
|
||||
{ TEXT("vta"), ImportModelFile::Import },
|
||||
{ TEXT("mdl"), ImportModelFile::Import },
|
||||
{ TEXT("md2"), ImportModelFile::Import },
|
||||
{ TEXT("md3"), ImportModelFile::Import },
|
||||
{ TEXT("md5mesh"), ImportModelFile::Import },
|
||||
{ TEXT("q3o"), ImportModelFile::Import },
|
||||
{ TEXT("q3s"), ImportModelFile::Import },
|
||||
{ TEXT("ac"), ImportModelFile::Import },
|
||||
{ TEXT("stl"), ImportModelFile::Import },
|
||||
{ TEXT("lwo"), ImportModelFile::Import },
|
||||
{ TEXT("lws"), ImportModelFile::Import },
|
||||
{ TEXT("lxo"), ImportModelFile::Import },
|
||||
{ TEXT("blend"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("bvh"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("ase"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("ply"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("dxf"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("ifc"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("nff"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("smd"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("vta"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("mdl"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("md2"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("md3"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("md5mesh"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("q3o"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("q3s"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("ac"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("stl"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("lwo"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("lws"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
{ TEXT("lxo"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
|
||||
};
|
||||
AssetsImportingManager::Importers.Add(InBuildImporters, ARRAY_COUNT(InBuildImporters));
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/Storage/JsonStorageProxy.h"
|
||||
#include "Engine/Content/Cache/AssetsCache.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Engine/Localization/LocalizedStringTable.h"
|
||||
#include "Engine/Utilities/TextProcessing.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
|
||||
bool CreateJson::Create(const StringView& path, rapidjson_flax::StringBuffer& data, const String& dataTypename)
|
||||
@@ -51,7 +55,7 @@ bool CreateJson::Create(const StringView& path, StringAnsiView& data, StringAnsi
|
||||
LOG(Warning, "Failed to create directory");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rapidjson_flax::StringBuffer buffer;
|
||||
@@ -88,8 +92,184 @@ bool CreateJson::Create(const StringView& path, StringAnsiView& data, StringAnsi
|
||||
{
|
||||
asset->Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
Content::GetRegistry()->RegisterAsset(id, String(dataTypename), path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FormatPoValue(String& value)
|
||||
{
|
||||
value.Replace(TEXT("\\n"), TEXT("\n"));
|
||||
value.Replace(TEXT("%s"), TEXT("{}"));
|
||||
value.Replace(TEXT("%d"), TEXT("{}"));
|
||||
}
|
||||
|
||||
CreateAssetResult CreateJson::ImportPo(CreateAssetContext& context)
|
||||
{
|
||||
// Base
|
||||
IMPORT_SETUP(LocalizedStringTable, 1);
|
||||
|
||||
// Load file (UTF-16)
|
||||
String inputData;
|
||||
if (File::ReadAllText(context.InputPath, inputData))
|
||||
{
|
||||
return CreateAssetResult::InvalidPath;
|
||||
}
|
||||
|
||||
// Use virtual asset for data storage and serialization
|
||||
AssetReference<LocalizedStringTable> asset = Content::CreateVirtualAsset<LocalizedStringTable>();
|
||||
if (!asset)
|
||||
return CreateAssetResult::Error;
|
||||
|
||||
// Parse PO format
|
||||
int32 pos = 0;
|
||||
int32 pluralCount = 0;
|
||||
int32 lineNumber = 0;
|
||||
bool fuzzy = false, hasNewContext = false;
|
||||
StringView msgctxt, msgid;
|
||||
String idTmp;
|
||||
while (pos < inputData.Length())
|
||||
{
|
||||
// Read line
|
||||
const int32 startPos = pos;
|
||||
while (pos < inputData.Length() && inputData[pos] != '\n')
|
||||
pos++;
|
||||
const StringView line(&inputData[startPos], pos - startPos);
|
||||
lineNumber++;
|
||||
pos++;
|
||||
const int32 valueStart = line.Find('\"') + 1;
|
||||
const int32 valueEnd = line.FindLast('\"');
|
||||
const StringView value(line.Get() + valueStart, Math::Max(valueEnd - valueStart, 0));
|
||||
|
||||
if (line.StartsWith(StringView(TEXT("msgid_plural"))))
|
||||
{
|
||||
// Plural form
|
||||
}
|
||||
else if (line.StartsWith(StringView(TEXT("msgid"))))
|
||||
{
|
||||
// Id
|
||||
msgid = value;
|
||||
|
||||
// Reset context if already used
|
||||
if (!hasNewContext)
|
||||
msgctxt = StringView();
|
||||
hasNewContext = false;
|
||||
}
|
||||
else if (line.StartsWith(StringView(TEXT("msgstr"))))
|
||||
{
|
||||
// String
|
||||
if (msgid.HasChars())
|
||||
{
|
||||
// Format message
|
||||
String msgstr(value);
|
||||
FormatPoValue(msgstr);
|
||||
|
||||
// Get message id
|
||||
StringView id = msgid;
|
||||
if (msgctxt.HasChars())
|
||||
{
|
||||
idTmp = String(msgctxt) + TEXT(".") + String(msgid);
|
||||
id = idTmp;
|
||||
}
|
||||
|
||||
int32 indexStart = line.Find('[');
|
||||
if (indexStart != -1 && indexStart < valueStart)
|
||||
{
|
||||
indexStart++;
|
||||
while (indexStart < line.Length() && StringUtils::IsWhitespace(line[indexStart]))
|
||||
indexStart++;
|
||||
int32 indexEnd = line.Find(']');
|
||||
while (indexEnd > indexStart && StringUtils::IsWhitespace(line[indexEnd - 1]))
|
||||
indexEnd--;
|
||||
int32 index = -1;
|
||||
StringUtils::Parse(line.Get() + indexStart, (uint32)(indexEnd - indexStart), &index);
|
||||
if (pluralCount <= 0)
|
||||
{
|
||||
LOG(Error, "Missing 'nplurals'. Cannot use plural message at line {0}", lineNumber);
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
if (index < 0 || index > pluralCount)
|
||||
{
|
||||
LOG(Error, "Invalid plural message index at line {0}", lineNumber);
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
|
||||
// Plural message
|
||||
asset->AddPluralString(id, msgstr, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Message
|
||||
asset->AddString(id, msgstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (line.StartsWith(StringView(TEXT("msgctxt"))))
|
||||
{
|
||||
// Context
|
||||
msgctxt = value;
|
||||
hasNewContext = true;
|
||||
}
|
||||
else if (line.StartsWith('\"'))
|
||||
{
|
||||
// Config
|
||||
const Char* pluralForms = StringUtils::Find(line.Get(), TEXT("Plural-Forms"));
|
||||
if (pluralForms != nullptr && pluralForms < line.Get() + line.Length() - 1)
|
||||
{
|
||||
// Process plural forms rule
|
||||
const Char* nplurals = StringUtils::Find(pluralForms, TEXT("nplurals"));
|
||||
if (nplurals && nplurals < line.Get() + line.Length())
|
||||
{
|
||||
while (*nplurals && *nplurals != '=')
|
||||
nplurals++;
|
||||
while (*nplurals && (StringUtils::IsWhitespace(*nplurals) || *nplurals == '='))
|
||||
nplurals++;
|
||||
const Char* npluralsStart = nplurals;
|
||||
while (*nplurals && !StringUtils::IsWhitespace(*nplurals) && *nplurals != ';')
|
||||
nplurals++;
|
||||
StringUtils::Parse(npluralsStart, (uint32)(nplurals - npluralsStart), &pluralCount);
|
||||
if (pluralCount < 0 || pluralCount > 100)
|
||||
{
|
||||
LOG(Error, "Invalid 'nplurals' at line {0}", lineNumber);
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
}
|
||||
// TODO: parse plural forms rule
|
||||
}
|
||||
const Char* language = StringUtils::Find(line.Get(), TEXT("Language"));
|
||||
if (language != nullptr && language < line.Get() + line.Length() - 1)
|
||||
{
|
||||
// Process language locale
|
||||
while (*language && *language != ':')
|
||||
language++;
|
||||
language++;
|
||||
while (*language && StringUtils::IsWhitespace(*language))
|
||||
language++;
|
||||
const Char* languageStart = language;
|
||||
while (*language && !StringUtils::IsWhitespace(*language) && *language != '\\' && *language != '\"')
|
||||
language++;
|
||||
asset->Locale.Set(languageStart, (int32)(language - languageStart));
|
||||
if (asset->Locale == TEXT("English"))
|
||||
asset->Locale = TEXT("en");
|
||||
if (asset->Locale.Length() > 5)
|
||||
LOG(Warning, "Imported .po file uses invalid locale '{0}'", asset->Locale);
|
||||
}
|
||||
}
|
||||
else if (line.StartsWith('#') || line.IsEmpty())
|
||||
{
|
||||
// Comment
|
||||
const Char* fuzzyPos = StringUtils::Find(line.Get(), TEXT("fuzzy"));
|
||||
fuzzy |= fuzzyPos != nullptr && fuzzyPos < line.Get() + line.Length() - 1;
|
||||
}
|
||||
}
|
||||
if (asset->Locale.IsEmpty())
|
||||
LOG(Warning, "Imported .po file has missing locale");
|
||||
|
||||
// Save asset
|
||||
return asset->Save(context.TargetAssetPath) ? CreateAssetResult::CannotSaveFile : CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
static bool Create(const StringView& path, rapidjson_flax::StringBuffer& data, const String& dataTypename);
|
||||
static bool Create(const StringView& path, rapidjson_flax::StringBuffer& data, const char* dataTypename);
|
||||
static bool Create(const StringView& path, StringAnsiView& data, StringAnsiView& dataTypename);
|
||||
static CreateAssetResult ImportPo(CreateAssetContext& context);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -111,12 +111,6 @@ public:
|
||||
/// <param name="writer">The json metadata writer.</param>
|
||||
void AddMeta(JsonWriter& writer) const;
|
||||
|
||||
/// <summary>
|
||||
/// Save asset file data to the hard drive
|
||||
/// </summary>
|
||||
/// <returns>Saving result</returns>
|
||||
CreateAssetResult Save();
|
||||
|
||||
private:
|
||||
|
||||
void ApplyChanges();
|
||||
@@ -130,12 +124,17 @@ struct AssetImporter
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Extension of the file to import with that importer
|
||||
/// Extension of the file to import with that importer (without leading dot).
|
||||
/// </summary>
|
||||
String FileExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Call asset importing process
|
||||
/// Extension of the output file as output with that importer (without leading dot).
|
||||
/// </summary>
|
||||
String ResultExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Callback for the asset importing process.
|
||||
/// </summary>
|
||||
CreateAssetFunction Callback;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Core/Memory/Memory.h"
|
||||
#include "Engine/Core/Memory/Allocation.h"
|
||||
@@ -47,6 +48,20 @@ public:
|
||||
_allocation.Allocate(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Array"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initList">The initial values defined in the array.</param>
|
||||
Array(std::initializer_list<T> initList)
|
||||
{
|
||||
_count = _capacity = (int32)initList.size();
|
||||
if (_count > 0)
|
||||
{
|
||||
_allocation.Allocate(_count);
|
||||
Memory::ConstructItems(Get(), initList.begin(), _count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Array"/> class.
|
||||
/// </summary>
|
||||
@@ -123,6 +138,24 @@ public:
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assignment operator that deletes the current collection of items and the copies items from the initializer list.
|
||||
/// </summary>
|
||||
/// <param name="initList">The other collection to copy.</param>
|
||||
/// <returns>The reference to this.</returns>
|
||||
Array& operator=(std::initializer_list<T> initList) noexcept
|
||||
{
|
||||
Memory::DestructItems(Get(), _count);
|
||||
|
||||
_count = _capacity = (int32)initList.size();
|
||||
if (_capacity > 0)
|
||||
{
|
||||
_allocation.Allocate(_capacity);
|
||||
Memory::ConstructItems(Get(), initList.begin(), _count);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assignment operator that deletes the current collection of items and the copies items from the other array.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Engine/Input/InputSettings.h"
|
||||
#include "Engine/Audio/AudioSettings.h"
|
||||
#include "Engine/Navigation/NavigationSettings.h"
|
||||
#include "Engine/Localization/LocalizationSettings.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
@@ -55,6 +56,8 @@ IMPLEMENT_SETTINGS_GETTER(PS4PlatformSettings, PS4Platform);
|
||||
IMPLEMENT_SETTINGS_GETTER(XboxScarlettPlatformSettings, XboxScarlettPlatform);
|
||||
#elif PLATFORM_ANDROID
|
||||
IMPLEMENT_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform);
|
||||
#elif PLATFORM_SWITCH
|
||||
IMPLEMENT_SETTINGS_GETTER(SwitchPlatformSettings, SwitchPlatform);
|
||||
#else
|
||||
#error Unknown platform
|
||||
#endif
|
||||
@@ -125,6 +128,7 @@ bool GameSettings::Load()
|
||||
PRELOAD_SETTINGS(Input);
|
||||
PRELOAD_SETTINGS(Graphics);
|
||||
PRELOAD_SETTINGS(Navigation);
|
||||
PRELOAD_SETTINGS(Localization);
|
||||
PRELOAD_SETTINGS(GameCooking);
|
||||
#undef PRELOAD_SETTINGS
|
||||
|
||||
@@ -156,6 +160,7 @@ void GameSettings::Apply()
|
||||
APPLY_SETTINGS(InputSettings);
|
||||
APPLY_SETTINGS(GraphicsSettings);
|
||||
APPLY_SETTINGS(NavigationSettings);
|
||||
APPLY_SETTINGS(LocalizationSettings);
|
||||
APPLY_SETTINGS(BuildSettings);
|
||||
APPLY_SETTINGS(PlatformSettings);
|
||||
#undef APPLY_SETTINGS
|
||||
@@ -195,6 +200,7 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
|
||||
DESERIALIZE(Input);
|
||||
DESERIALIZE(Graphics);
|
||||
DESERIALIZE(Navigation);
|
||||
DESERIALIZE(Localization);
|
||||
DESERIALIZE(GameCooking);
|
||||
|
||||
// Per-platform settings containers
|
||||
@@ -204,6 +210,7 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
|
||||
DESERIALIZE(PS4Platform);
|
||||
DESERIALIZE(XboxScarlettPlatform);
|
||||
DESERIALIZE(AndroidPlatform);
|
||||
DESERIALIZE(SwitchPlatform);
|
||||
}
|
||||
|
||||
void LayersAndTagsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace FlaxEditor.Content.Settings
|
||||
{
|
||||
internal const string PS4PlatformSettingsTypename = "FlaxEditor.Content.Settings.PS4PlatformSettings";
|
||||
internal const string XboxScarlettPlatformSettingsTypename = "FlaxEditor.Content.Settings.XboxScarlettPlatformSettings";
|
||||
internal const string SwitchPlatformSettingsTypename = "FlaxEditor.Content.Settings.SwitchPlatformSettings";
|
||||
|
||||
/// <summary>
|
||||
/// The default application icon.
|
||||
@@ -78,6 +79,12 @@ namespace FlaxEditor.Content.Settings
|
||||
[EditorOrder(1045), EditorDisplay("Other Settings"), AssetReference(typeof(NavigationSettings), true), Tooltip("Reference to Navigation Settings asset")]
|
||||
public JsonAsset Navigation;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="LocalizationSettings"/> asset.
|
||||
/// </summary>
|
||||
[EditorOrder(1046), EditorDisplay("Other Settings"), AssetReference(typeof(LocalizationSettings), true), Tooltip("Reference to Localization Settings asset")]
|
||||
public JsonAsset Localization;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to <see cref="BuildSettings"/> asset.
|
||||
/// </summary>
|
||||
@@ -138,6 +145,14 @@ namespace FlaxEditor.Content.Settings
|
||||
public JsonAsset AndroidPlatform;
|
||||
#endif
|
||||
|
||||
#if FLAX_EDITOR || PLATFORM_SWITCH
|
||||
/// <summary>
|
||||
/// Reference to Switch Platform Settings asset. Used to apply configuration on Switch platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2070), EditorDisplay("Platform Settings", "Switch"), AssetReference(SwitchPlatformSettingsTypename, true), Tooltip("Reference to Switch Platform Settings asset")]
|
||||
public JsonAsset SwitchPlatform;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the game settings asset file.
|
||||
/// </summary>
|
||||
@@ -210,6 +225,8 @@ namespace FlaxEditor.Content.Settings
|
||||
return LoadAsset<GraphicsSettings>(gameSettings.Graphics) as T;
|
||||
if (type == typeof(NavigationSettings))
|
||||
return LoadAsset<NavigationSettings>(gameSettings.Navigation) as T;
|
||||
if (type == typeof(LocalizationSettings))
|
||||
return LoadAsset<LocalizationSettings>(gameSettings.Localization) as T;
|
||||
if (type == typeof(BuildSettings))
|
||||
return LoadAsset<BuildSettings>(gameSettings.GameCooking) as T;
|
||||
if (type == typeof(InputSettings))
|
||||
@@ -240,6 +257,10 @@ namespace FlaxEditor.Content.Settings
|
||||
if (type == typeof(AndroidPlatformSettings))
|
||||
return LoadAsset<AndroidPlatformSettings>(gameSettings.AndroidPlatform) as T;
|
||||
#endif
|
||||
#if FLAX_EDITOR || PLATFORM_SWITCH
|
||||
if (type.FullName == SwitchPlatformSettingsTypename)
|
||||
return LoadAsset(gameSettings.SwitchPlatform, SwitchPlatformSettingsTypename) as T;
|
||||
#endif
|
||||
|
||||
if (gameSettings.CustomSettings != null)
|
||||
{
|
||||
@@ -308,6 +329,8 @@ namespace FlaxEditor.Content.Settings
|
||||
return SaveAsset(gameSettings, ref gameSettings.Graphics, obj);
|
||||
if (type == typeof(NavigationSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Navigation, obj);
|
||||
if (type == typeof(LocalizationSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Localization, obj);
|
||||
if (type == typeof(BuildSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.GameCooking, obj);
|
||||
if (type == typeof(InputSettings))
|
||||
@@ -324,6 +347,8 @@ namespace FlaxEditor.Content.Settings
|
||||
return SaveAsset(gameSettings, ref gameSettings.XboxScarlettPlatform, obj);
|
||||
if (type == typeof(AndroidPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.AndroidPlatform, obj);
|
||||
if (type.FullName == SwitchPlatformSettingsTypename)
|
||||
return SaveAsset(gameSettings, ref gameSettings.SwitchPlatform, obj);
|
||||
if (type == typeof(AudioSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.Audio, obj);
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ public:
|
||||
Guid Input;
|
||||
Guid Graphics;
|
||||
Guid Navigation;
|
||||
Guid Localization;
|
||||
Guid GameCooking;
|
||||
|
||||
// Per-platform settings containers
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
Guid PS4Platform;
|
||||
Guid XboxScarlettPlatform;
|
||||
Guid AndroidPlatform;
|
||||
Guid SwitchPlatform;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Config/Settings.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
|
||||
/// <summary>
|
||||
/// Layers and objects tags settings.
|
||||
|
||||
@@ -23,3 +23,6 @@
|
||||
#if PLATFORM_ANDROID
|
||||
#include "Engine/Platform/Android/AndroidPlatformSettings.h"
|
||||
#endif
|
||||
#if PLATFORM_SWITCH
|
||||
#include "Platforms/Switch/Engine/Platform/SwitchPlatformSettings.h"
|
||||
#endif
|
||||
|
||||
@@ -15,31 +15,31 @@ public:
|
||||
/// <summary>
|
||||
/// The target amount of the game logic updates per second (script updates frequency).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(1), DefaultValue(30.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Update FPS\")")
|
||||
float UpdateFPS = 30.0f;
|
||||
API_FIELD(Attributes="EditorOrder(1), Limit(0, 1000), EditorDisplay(\"General\", \"Update FPS\")")
|
||||
float UpdateFPS = 60.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The target amount of the physics simulation updates per second (also fixed updates frequency).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(2), DefaultValue(60.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Physics FPS\")")
|
||||
API_FIELD(Attributes="EditorOrder(2), Limit(0, 1000), EditorDisplay(\"General\", \"Physics FPS\")")
|
||||
float PhysicsFPS = 60.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The target amount of the frames rendered per second (actual game FPS).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(3), DefaultValue(60.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Draw FPS\")")
|
||||
API_FIELD(Attributes="EditorOrder(3), Limit(0, 1000), EditorDisplay(\"General\", \"Draw FPS\")")
|
||||
float DrawFPS = 60.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The game time scale factor. Default is 1.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(1.0f), Limit(0, 1000.0f, 0.1f), EditorDisplay(\"General\")")
|
||||
API_FIELD(Attributes="EditorOrder(10), Limit(0, 1000.0f, 0.1f), EditorDisplay(\"General\")")
|
||||
float TimeScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum allowed delta time (in seconds) for the game logic update step.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.1f), Limit(0.1f, 1000.0f, 0.01f), EditorDisplay(\"General\")")
|
||||
API_FIELD(Attributes="EditorOrder(20), Limit(0.1f, 1000.0f, 0.01f), EditorDisplay(\"General\")")
|
||||
float MaxUpdateDeltaTime = 0.1f;
|
||||
|
||||
public:
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
|
||||
#include "Engine/Core/Memory/Allocation.h"
|
||||
|
||||
template<typename>
|
||||
class Function;
|
||||
template<typename... Params>
|
||||
class Delegate;
|
||||
|
||||
/// <summary>
|
||||
/// The function object.
|
||||
/// </summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user