Merge branch '1.2' into IconOverhaul

This commit is contained in:
W2.Wizard
2021-05-13 15:21:36 +02:00
committed by GitHub
577 changed files with 26583 additions and 26529 deletions

Binary file not shown.

View File

@@ -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.",

View File

@@ -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">(?&lt;=\W|^)(?&lt;TAG&gt;\[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>

View File

@@ -10,6 +10,7 @@
#include "Editor/Editor.h"
#include "Editor/ProjectInfo.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Utilities/StringConverter.h"
#include "FlaxEngine.Gen.h"

View File

@@ -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>

View File

@@ -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()

View File

@@ -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.

View File

@@ -75,6 +75,7 @@ namespace FlaxEditor.Content
}
}
/// <inheritdoc />
public int MetadataToken => 0;
/// <inheritdoc />

View File

@@ -76,7 +76,7 @@ void PreviewsCache::FlushTask::OnEnd()
ThreadPoolTask::OnEnd();
}
REGISTER_BINARY_ASSET(PreviewsCache, "FlaxEditor.PreviewsCache", ::New<TextureAssetUpgrader>(), false);
REGISTER_BINARY_ASSET_WITH_UPGRADER(PreviewsCache, "FlaxEditor.PreviewsCache", TextureAssetUpgrader, false);
PreviewsCache::PreviewsCache(const SpawnParams& params, const AssetInfo* info)
: SpriteAtlas(params, info)

View File

@@ -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)
{

View 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";
}
}

View File

@@ -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.

View File

@@ -10,6 +10,7 @@
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Content/Content.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Threading/ThreadSpawner.h"
#include "Engine/Platform/FileSystem.h"
#include "Steps/ValidateStep.h"
@@ -39,7 +40,6 @@
#endif
#if PLATFORM_TOOLS_PS4
#include "Platforms/PS4/Editor/PlatformTools/PS4PlatformTools.h"
#include "Platforms/PS4/Engine/Platform/PS4PlatformSettings.h"
#endif
#if PLATFORM_TOOLS_XBOX_SCARLETT
#include "Platforms/XboxScarlett/Editor/PlatformTools/XboxScarlettPlatformTools.h"
@@ -47,6 +47,9 @@
#if PLATFORM_TOOLS_ANDROID
#include "Platform/Android/AndroidPlatformTools.h"
#endif
#if PLATFORM_TOOLS_SWITCH
#include "Platforms/Switch/Editor/PlatformTools/SwitchPlatformTools.h"
#endif
namespace GameCookerImpl
{
@@ -89,6 +92,50 @@ using namespace GameCookerImpl;
Delegate<GameCooker::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 +297,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 +334,10 @@ void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
data.Configuration = configuration;
data.Options = options;
data.CustomDefines = customDefines;
data.OutputPath = outputPath;
FileSystem::NormalizePath(data.OutputPath);
data.OutputPath = data.OriginalOutputPath = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, data.OutputPath);
data.OriginalOutputPath = outputPath;
FileSystem::NormalizePath(data.OriginalOutputPath);
data.OriginalOutputPath = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, data.OriginalOutputPath);
data.CodeOutputPath = data.DataOutputPath = data.OriginalOutputPath;
data.CacheDirectory = Globals::ProjectCacheFolder / TEXT("Cooker") / tools->GetName();
if (!FileSystem::DirectoryExists(data.CacheDirectory))
{
@@ -367,7 +420,7 @@ bool GameCookerImpl::Build()
CookingData& data = Data;
LOG(Info, "Starting Game Cooker...");
LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration));
LOG(Info, "Output Path: {0}", data.OutputPath);
LOG(Info, "Output Path: {0}", data.OriginalOutputPath);
// Late init feature
if (Steps.IsEmpty())

View File

@@ -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;

View File

@@ -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
{

View File

@@ -25,7 +25,7 @@ bool UWPPlatformTools::OnScriptsStepDone(CookingData& data)
{
// Override Newtonsoft.Json.dll for some platforms (that don't support runtime code generation)
const String customBinPath = data.GetPlatformBinariesRoot() / TEXT("Newtonsoft.Json.dll");
const String assembliesPath = data.OutputPath;
const String assembliesPath = data.CodeOutputPath;
if (FileSystem::CopyFile(assembliesPath / TEXT("Newtonsoft.Json.dll"), customBinPath))
{
data.Error(TEXT("Failed to copy deploy custom assembly."));
@@ -43,7 +43,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
const auto uwpDataPath = platformDataPath / (isXboxOne ? TEXT("XboxOne") : TEXT("UWP")) / TEXT("Binaries");
const auto gameSettings = GameSettings::Get();
const auto platformSettings = UWPPlatformSettings::Get();
Array<byte> fileTemplate;
StringAnsi fileTemplate;
// Copy binaries
const auto binPath = data.GetGameBinariesPath();
@@ -64,7 +64,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
return true;
}
if (FileSystem::CopyFile(data.OutputPath / StringUtils::GetFileName(files[i]), files[i]))
if (FileSystem::CopyFile(data.DataOutputPath / StringUtils::GetFileName(files[i]), files[i]))
{
data.Error(TEXT("Failed to setup output directory."));
return true;
@@ -92,7 +92,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
// Prepare certificate
const auto srcCertificatePath = Globals::ProjectFolder / platformSettings->CertificateLocation;
const auto dstCertificatePath = data.OutputPath / TEXT("WSACertificate.pfx");
const auto dstCertificatePath = data.DataOutputPath / TEXT("WSACertificate.pfx");
if (platformSettings->CertificateLocation.HasChars() && FileSystem::FileExists(srcCertificatePath))
{
// Use cert from settings
@@ -115,7 +115,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
// Copy assets
const auto dstAssetsPath = data.OutputPath / TEXT("Assets");
const auto dstAssetsPath = data.DataOutputPath / TEXT("Assets");
const auto srcAssetsPath = uwpDataPath / TEXT("Assets");
if (!FileSystem::DirectoryExists(dstAssetsPath))
{
@@ -125,7 +125,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
return true;
}
}
const auto dstPropertiesPath = data.OutputPath / TEXT("Properties");
const auto dstPropertiesPath = data.DataOutputPath / TEXT("Properties");
if (!FileSystem::DirectoryExists(dstPropertiesPath))
{
if (FileSystem::CreateDirectory(dstPropertiesPath))
@@ -149,12 +149,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (!FileSystem::FileExists(dstAssemblyInfoPath))
{
// Get template
if (File::ReadAllBytes(srcAssemblyInfoPath, fileTemplate))
if (File::ReadAllText(srcAssemblyInfoPath, fileTemplate))
{
data.Error(TEXT("Failed to load AssemblyInfo.cs template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstAssemblyInfoPath);
@@ -163,7 +162,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
{
auto now = DateTime::Now();
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, gameSettings->ProductName.ToStringAnsi()
, gameSettings->CompanyName.ToStringAnsi()
, now.GetYear()
@@ -177,17 +176,16 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
return true;
}
}
const auto dstAppPath = data.OutputPath / TEXT("App.cs");
const auto dstAppPath = data.DataOutputPath / TEXT("App.cs");
const auto srcAppPath = uwpDataPath / TEXT("App.cs");
if (!FileSystem::FileExists(dstAppPath))
{
// Get template
if (File::ReadAllBytes(srcAppPath, fileTemplate))
if (File::ReadAllText(srcAppPath, fileTemplate))
{
data.Error(TEXT("Failed to load App.cs template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstAppPath);
@@ -195,7 +193,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, defaultNamespace.ToStringAnsi() // {0} Default Namespace
);
hasError = file->HasError();
@@ -207,16 +205,15 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
return true;
}
}
const auto dstFlaxGeneratedPath = data.OutputPath / TEXT("FlaxGenerated.cs");
const auto dstFlaxGeneratedPath = data.DataOutputPath / TEXT("FlaxGenerated.cs");
const auto srcFlaxGeneratedPath = uwpDataPath / TEXT("FlaxGenerated.cs");
{
// Get template
if (File::ReadAllBytes(srcFlaxGeneratedPath, fileTemplate))
if (File::ReadAllText(srcFlaxGeneratedPath, fileTemplate))
{
data.Error(TEXT("Failed to load FlaxGenerated.cs template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Prepare
StringAnsi autoRotationPreferences;
@@ -252,7 +249,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, autoRotationPreferences.Get()
, preferredLaunchWindowingMode.Get()
);
@@ -267,17 +264,16 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
// Create solution
const auto dstSolutionPath = data.OutputPath / projectName + TEXT(".sln");
const auto dstSolutionPath = data.DataOutputPath / projectName + TEXT(".sln");
const auto srcSolutionPath = uwpDataPath / TEXT("Solution.sln");
if (!FileSystem::FileExists(dstSolutionPath))
{
// Get template
if (File::ReadAllBytes(srcSolutionPath, fileTemplate))
if (File::ReadAllText(srcSolutionPath, fileTemplate))
{
data.Error(TEXT("Failed to load Solution.sln template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstSolutionPath);
@@ -285,7 +281,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Project Name
, mode // {1} Platform Mode
, projectGuid.ToStringAnsi() // {2} Project ID
@@ -301,16 +297,15 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
// Create project
const auto dstProjectPath = data.OutputPath / projectName + TEXT(".csproj");
const auto dstProjectPath = data.DataOutputPath / projectName + TEXT(".csproj");
const auto srcProjectPath = uwpDataPath / TEXT("Project.csproj");
{
// Get template
if (File::ReadAllBytes(srcProjectPath, fileTemplate))
if (File::ReadAllText(srcProjectPath, fileTemplate))
{
data.Error(TEXT("Failed to load Project.csproj template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Build included files data
StringBuilder filesInclude(2048);
@@ -334,7 +329,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Project Name
, mode // {1} Platform Mode
, projectGuid.Get() // {2} Project ID
@@ -352,17 +347,16 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
}
// Create manifest
const auto dstManifestPath = data.OutputPath / TEXT("Package.appxmanifest");
const auto dstManifestPath = data.DataOutputPath / TEXT("Package.appxmanifest");
const auto srcManifestPath = uwpDataPath / TEXT("Package.appxmanifest");
if (!FileSystem::FileExists(dstManifestPath))
{
// Get template
if (File::ReadAllBytes(srcManifestPath, fileTemplate))
if (File::ReadAllText(srcManifestPath, fileTemplate))
{
data.Error(TEXT("Failed to load Package.appxmanifest template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Build included files data
StringBuilder filesInclude(2048);
@@ -385,7 +379,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Display Name
, gameSettings->CompanyName.ToStringAnsi() // {1} Company Name
, productId.ToStringAnsi() // {2} Product ID
@@ -490,8 +484,8 @@ bool UWPPlatformTools::OnPostProcess(CookingData& data)
// Special case for UWP
// FlaxEngine.dll cannot be added to the solution as `Content` item (due to conflicts with C++ /CX FlaxEngine.dll)
// Use special directory for it (generated UWP project handles this case and copies lib to the output)
const String assembliesPath = data.OutputPath;
const auto dstPath1 = data.OutputPath / TEXT("DataSecondary");
const String assembliesPath = data.DataOutputPath;
const auto dstPath1 = data.DataOutputPath / TEXT("DataSecondary");
if (!FileSystem::DirectoryExists(dstPath1))
{
if (FileSystem::CreateDirectory(dstPath1))

View File

@@ -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;

View File

@@ -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"));
}
};

View File

@@ -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);

View File

@@ -29,6 +29,7 @@
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Engine/Base/GameBase.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
#if PLATFORM_TOOLS_WINDOWS
#include "Engine/Platform/Windows/WindowsPlatformSettings.h"
@@ -453,6 +454,14 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE);
break;
}
#endif
#if PLATFORM_TOOLS_SWITCH
case BuildPlatform::Switch:
{
const char* platformDefineName = "PLATFORM_SWITCH";
COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE);
break;
}
#endif
default:
{
@@ -861,7 +870,7 @@ public:
// Create package
// Note: FlaxStorage::Create overrides chunks locations in file so don't use files anymore (only readonly)
const String localPath = String::Format(TEXT("Content/Data_{0}.{1}"), _packageIndex, PACKAGE_FILES_EXTENSION);
const String path = data.OutputPath / localPath;
const String path = data.DataOutputPath / localPath;
if (FlaxStorage::Create(path, assetsData, false, &CustomData))
{
data.Error(TEXT("Failed to create assets package."));
@@ -1027,7 +1036,7 @@ bool CookAssetsStep::Perform(CookingData& data)
gameFlags |= GameHeaderFlags::ShowSplashScreen;
// Open file
auto stream = FileWriteStream::Open(data.OutputPath / TEXT("Content/head"));
auto stream = FileWriteStream::Open(data.DataOutputPath / TEXT("Content/head"));
if (stream == nullptr)
{
data.Error(TEXT("Failed to create game data file."));
@@ -1121,7 +1130,7 @@ bool CookAssetsStep::Perform(CookingData& data)
BUILD_STEP_CANCEL_CHECK;
// Save assets cache
if (AssetsCache::Save(data.OutputPath / TEXT("Content/AssetsCache.dat"), AssetsRegistry, AssetPathsMapping, AssetsCacheFlags::RelativePaths))
if (AssetsCache::Save(data.DataOutputPath / TEXT("Content/AssetsCache.dat"), AssetsRegistry, AssetPathsMapping, AssetsCacheFlags::RelativePaths))
{
data.Error(TEXT("Failed to create assets registry."));
return true;

View File

@@ -7,6 +7,7 @@
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Renderer/ReflectionsPass.h"
#include "Engine/Renderer/AntiAliasing/SMAA.h"
#include "Engine/Engine/Globals.h"
bool DeployDataStep::Perform(CookingData& data)
{
@@ -15,7 +16,7 @@ bool DeployDataStep::Perform(CookingData& data)
const auto gameSettings = GameSettings::Get();
// Setup output folders and copy required data
const auto contentDir = data.OutputPath / TEXT("Content");
const auto contentDir = data.DataOutputPath / TEXT("Content");
if (FileSystem::DirectoryExists(contentDir))
{
// Remove old content files
@@ -26,7 +27,7 @@ bool DeployDataStep::Perform(CookingData& data)
}
FileSystem::CreateDirectory(contentDir);
const auto srcMono = depsRoot / TEXT("Mono");
const auto dstMono = data.OutputPath / TEXT("Mono");
const auto dstMono = data.DataOutputPath / TEXT("Mono");
if (!FileSystem::DirectoryExists(dstMono))
{
if (!FileSystem::DirectoryExists(srcMono))

View File

@@ -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++)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -3,6 +3,7 @@
#include "CustomEditorsUtil.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/DateTime.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Scripting/Scripting.h"

View File

@@ -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();
}
}
}

View File

@@ -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);

View File

@@ -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;

View 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;
}
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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)

View 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();
}
}
}

View File

@@ -3,7 +3,6 @@
using System;
using FlaxEditor.GUI;
using FlaxEditor.Scripting;
using FlaxEngine;
namespace FlaxEditor.CustomEditors.Editors
{

View File

@@ -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()
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -46,10 +46,7 @@ namespace FlaxEditor.CustomEditors
if (values.HasReferenceValue)
{
var v = (IList)values.ReferenceValue;
// Get the reference value if collections are the same size
if (v != null && values.Count == v.Count)
if (values.ReferenceValue is IList v && values.Count == v.Count && v.Count > index)
{
_referenceValue = v[index];
_hasReferenceValue = true;

View File

@@ -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");

View File

@@ -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);

View File

@@ -29,7 +29,7 @@ namespace FlaxEditor.GUI
/// <summary>
/// The cached value from the UI.
/// </summary>
protected int _cachedValue;
protected long _cachedValue;
/// <summary>
/// True if has value cached, otherwise false.
@@ -54,7 +54,7 @@ namespace FlaxEditor.GUI
/// <summary>
/// The value.
/// </summary>
public int Value;
public long 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, long value, string tooltip = null)
{
Name = name;
Tooltip = tooltip;
@@ -88,13 +88,13 @@ namespace FlaxEditor.GUI
public object EnumTypeValue
{
get => Enum.ToObject(_enumType, Value);
set => Value = Convert.ToInt32(value);
set => Value = Convert.ToInt64(value);
}
/// <summary>
/// Gets or sets the value.
/// </summary>
public int Value
public long Value
{
get => _cachedValue;
set
@@ -209,13 +209,13 @@ namespace FlaxEditor.GUI
/// </summary>
protected void CacheValue()
{
int value = 0;
long value = 0;
if (IsFlags)
{
var selection = Selection;
for (int i = 0; i < selection.Count; i++)
{
int index = selection[i];
var index = selection[i];
value |= _entries[index].Value;
}
}
@@ -276,7 +276,7 @@ namespace FlaxEditor.GUI
tooltip = tooltipAttr.Text;
}
entries.Add(new Entry(name, Convert.ToInt32(field.GetRawConstantValue()), tooltip));
entries.Add(new Entry(name, Convert.ToInt64(field.GetRawConstantValue()), tooltip));
}
}
@@ -295,9 +295,9 @@ namespace FlaxEditor.GUI
}
// Calculate value that will be set after change
int valueAfter = 0;
long valueAfter = 0;
bool isSelected = _selectedIndices.Contains(index);
int selectedValue = entries[index].Value;
long selectedValue = entries[index].Value;
for (int i = 0; i < _selectedIndices.Count; i++)
{
int selectedIndex = _selectedIndices[i];

View File

@@ -89,6 +89,8 @@ namespace FlaxEditor.GUI
new PlatformData(PlatformType.PS4, icons.PS4Icon128, "PlayStation 4"),
new PlatformData(PlatformType.XboxScarlett, icons.XBoxScarletIcon128, "Xbox Scarlett"),
new PlatformData(PlatformType.Android, icons.AndroidIcon128, "Android"),
new PlatformData(PlatformType.Switch, icons.ColorWheel128, "Switch"),
};
const float IconSize = 48.0f;

View File

@@ -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; }

View File

@@ -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++)

View File

@@ -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)

View File

@@ -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" },

View File

@@ -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,
});

View File

@@ -22,14 +22,25 @@ namespace FlaxEditor.Modules
/// <seealso cref="FlaxEditor.SceneGraph.RootNode" />
public class ScenesRootNode : RootNode
{
private readonly Editor _editor;
/// <inheritdoc />
public ScenesRootNode()
{
_editor = Editor.Instance;
}
/// <inheritdoc />
public override void Spawn(Actor actor, Actor parent)
{
Editor.Instance.SceneEditing.Spawn(actor, parent);
_editor.SceneEditing.Spawn(actor, parent);
}
/// <inheritdoc />
public override Undo Undo => Editor.Instance.Undo;
/// <inheritdoc />
public override List<SceneGraphNode> Selection => _editor.SceneEditing.Selection;
}
/// <summary>

View File

@@ -150,7 +150,10 @@ namespace FlaxEditor.Options
private void Save()
{
// Update file
Editor.SaveJsonAsset(_optionsFilePath, Options);
if (Editor.SaveJsonAsset(_optionsFilePath, Options))
{
MessageBox.Show(string.Format("Failed to save editor option to '{0}'. Ensure that directory exists and program has access to it.", _optionsFilePath), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Special case for editor analytics
var editorAnalyticsTrackingFile = Path.Combine(Editor.LocalCachePath, "noTracking");

View File

@@ -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

View File

@@ -29,7 +29,7 @@ namespace FlaxEditor.SceneGraph.Actors
public override bool CanBeSelectedDirectly => true;
public override bool CanDuplicate => true;
public override bool CanDuplicate => !Root?.Selection.Contains(ParentNode) ?? true;
public override bool CanDelete => true;

View File

@@ -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();
}
}
}
}

View File

@@ -86,6 +86,10 @@ namespace FlaxEditor.SceneGraph.GUI
}
parent.SortChildren();
}
else if (Actor)
{
_orderInParent = Actor.OrderInParent;
}
}
internal void OnNameChanged()

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
@@ -146,5 +147,10 @@ namespace FlaxEditor.SceneGraph
/// Gets the undo.
/// </summary>
public abstract Undo Undo { get; }
/// <summary>
/// Gets the list of selected scene graph nodes in the editor context.
/// </summary>
public abstract List<SceneGraphNode> Selection { get; }
}
}

View File

@@ -57,7 +57,9 @@ namespace FlaxEditor.SceneGraph
/// </summary>
public virtual RootNode Root => ParentNode?.Root;
/// <inheritdoc />
/// <summary>
/// Gets or sets the transform of the node.
/// </summary>
public abstract Transform Transform { get; set; }
/// <summary>
@@ -110,18 +112,9 @@ namespace FlaxEditor.SceneGraph
{
if (parentNode != value)
{
if (parentNode != null)
{
parentNode.ChildNodes.Remove(this);
}
parentNode?.ChildNodes.Remove(this);
parentNode = value;
if (parentNode != null)
{
parentNode.ChildNodes.Add(this);
}
parentNode?.ChildNodes.Add(this);
OnParentChanged();
}
}
@@ -372,6 +365,7 @@ namespace FlaxEditor.SceneGraph
/// <summary>
/// Gets or sets the node state.
/// </summary>
[NoSerialize]
public virtual StateData State
{
get => throw new NotImplementedException();

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Types/StringBuilder.h"
#include "Engine/Debug/Exceptions/FileNotFoundException.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/FileSystemWatcher.h"
#include "Engine/Threading/ThreadPool.h"

View File

@@ -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

View File

@@ -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

View File

@@ -576,7 +576,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < _parameters.Length; i++)
{
writer.Write(_parameters[i].Name); // Parameter name
Utilities.Utils.WriteVariantType(writer, TypeUtils.GetType(_parameters[i].Type)); // Box type
Utilities.VariantUtils.WriteVariantType(writer, TypeUtils.GetType(_parameters[i].Type)); // Box type
}
SetValue(2, stream.ToArray());
}
@@ -594,7 +594,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < parametersCount; i++)
{
var parameterName = reader.ReadString(); // Parameter name
var boxType = Utilities.Utils.ReadVariantType(reader); // Box type
var boxType = Utilities.VariantUtils.ReadVariantType(reader); // Box type
MakeBox(i + 1, parameterName, boxType);
}
}
@@ -778,14 +778,14 @@ namespace FlaxEditor.Surface.Archetypes
{
reader.ReadByte(); // Version
signature.IsStatic = reader.ReadBoolean(); // Is Static
signature.ReturnType = Utilities.Utils.ReadVariantScriptType(reader); // Return type
signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return type
var parametersCount = reader.ReadInt32(); // Parameters count
signature.Params = parametersCount != 0 ? new SignatureParamInfo[parametersCount] : Utils.GetEmptyArray<SignatureParamInfo>();
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref signature.Params[i];
param.Name = Utilities.Utils.ReadStr(reader, 11); // Parameter name
param.Type = Utilities.Utils.ReadVariantScriptType(reader); // Parameter type
param.Type = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.IsOut = reader.ReadByte() != 0; // Is parameter out
}
}
@@ -799,14 +799,14 @@ namespace FlaxEditor.Surface.Archetypes
{
reader.ReadByte(); // Version
signature.IsStatic = reader.ReadBoolean(); // Is Static
signature.ReturnType = Utilities.Utils.ReadVariantScriptType(reader); // Return type
signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return type
var parametersCount = reader.ReadInt32(); // Parameters count
signature.Params = parametersCount != 0 ? new SignatureParamInfo[parametersCount] : Utils.GetEmptyArray<SignatureParamInfo>();
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref signature.Params[i];
param.Name = reader.ReadString(); // Parameter name
param.Type = Utilities.Utils.ReadVariantScriptType(reader); // Parameter type
param.Type = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.IsOut = reader.ReadByte() != 0; // Is parameter out
}
}
@@ -823,13 +823,13 @@ namespace FlaxEditor.Surface.Archetypes
{
writer.Write((byte)4); // Version
writer.Write(methodInfo.IsStatic); // Is Static
Utilities.Utils.WriteVariantType(writer, methodInfo.ValueType); // Return type
Utilities.VariantUtils.WriteVariantType(writer, methodInfo.ValueType); // Return type
writer.Write(parameters.Length); // Parameters count
for (int i = 0; i < parameters.Length; i++)
{
ref var param = ref parameters[i];
Utilities.Utils.WriteStr(writer, param.Name, 11); // Parameter name
Utilities.Utils.WriteVariantType(writer, param.Type); // Parameter type
Utilities.VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
writer.Write((byte)(param.IsOut ? 1 : 0)); // Is parameter out
}
return stream.ToArray();
@@ -1434,14 +1434,14 @@ namespace FlaxEditor.Surface.Archetypes
if (_signature.IsVirtual)
flags |= Flags.Virtual;
writer.Write((byte)flags); // Flags
Utilities.Utils.WriteVariantType(writer, _signature.ReturnType); // Return Type
Utilities.VariantUtils.WriteVariantType(writer, _signature.ReturnType); // Return Type
var parametersCount = _signature.Parameters?.Length ?? 0;
writer.Write(parametersCount); // Parameters count
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref _signature.Parameters[i];
Utilities.Utils.WriteStrAnsi(writer, param.Name, 13); // Parameter name
Utilities.Utils.WriteVariantType(writer, param.Type); // Parameter type
Utilities.VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
writer.Write((byte)0); // Is parameter out
writer.Write((byte)0); // Has default value
}
@@ -1470,13 +1470,13 @@ namespace FlaxEditor.Surface.Archetypes
var flags = (Flags)reader.ReadByte(); // Flags
_signature.IsStatic = (flags & Flags.Static) == Flags.Static;
_signature.IsVirtual = (flags & Flags.Virtual) == Flags.Virtual;
_signature.ReturnType = Utilities.Utils.ReadVariantScriptType(reader); // Return Type
_signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return Type
var parametersCount = reader.ReadInt32(); // Parameters count
_signature.Parameters = new Parameter[parametersCount];
for (int i = 0; i < parametersCount; i++)
{
var paramName = Utilities.Utils.ReadStrAnsi(reader, 13); // Parameter name
var paramType = Utilities.Utils.ReadVariantScriptType(reader); // Parameter type
var paramType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
var isOut = reader.ReadByte() != 0; // Is parameter out
var hasDefaultValue = reader.ReadByte() != 0; // Has default value
_signature.Parameters[i] = new Parameter

View File

@@ -171,7 +171,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < fieldsLength; i++)
{
Utilities.Utils.WriteStr(writer, fields[i].Name, 11); // Field type
Utilities.Utils.WriteVariantType(writer, fields[i].ValueType); // Field type
Utilities.VariantUtils.WriteVariantType(writer, fields[i].ValueType); // Field type
}
Values[1] = stream.ToArray();
}
@@ -188,7 +188,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < fieldsLength; i++)
{
var fieldName = Utilities.Utils.ReadStr(reader, 11); // Field name
var fieldType = Utilities.Utils.ReadVariantType(reader); // Field type
var fieldType = Utilities.VariantUtils.ReadVariantType(reader); // Field type
MakeBox(i + 1, fieldName, new ScriptType(fieldType));
}
}

View File

@@ -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),

View File

@@ -38,7 +38,7 @@ namespace FlaxEditor.Surface.Elements
/// <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);

View File

@@ -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>

View File

@@ -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 />

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "EditorUtilities.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Core/Log.h"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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();

View File

@@ -26,6 +26,7 @@ namespace FlaxEditor.Viewport
/// <seealso cref="IGizmoOwner" />
public class PrefabWindowViewport : PrefabPreview, IEditorPrimitivesOwner
{
[HideInEditor]
private sealed class PrefabSpritesRenderer : MainEditorGizmoViewport.EditorSpritesRenderer
{
public PrefabWindowViewport Viewport;

View 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();
}
}
}

View File

@@ -131,6 +131,7 @@ namespace FlaxEditor.Windows
"LZ4 Library - Copyright (c) Yann Collet. All rights reserved.",
"fmt - www.fmtlib.net",
"minimp3 - www.github.com/lieff/minimp3",
"Tracy Profiler - www.github.com/wolfpld/tracy",
"Ogg and Vorbis - Xiph.org Foundation",
"OpenAL Soft - www.github.com/kcat/openal-soft",
"OpenFBX - www.github.com/nem0/OpenFBX",

View File

@@ -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();
}

View File

@@ -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.Save64, Save).LinkTooltip("Save");
_toolstrip.AddSeparator();
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _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)

View 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);
}
}
}

View File

@@ -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)

View File

@@ -36,6 +36,9 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
public override Undo Undo => _window.Undo;
/// <inheritdoc />
public override List<SceneGraphNode> Selection => _window.Selection;
}
/// <summary>

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -5,6 +5,29 @@ using FlaxEditor.GUI;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEngine
{
partial class ProfilerCPU
{
partial struct Event
{
/// <summary>
/// Gets the event name.
/// </summary>
public unsafe string Name
{
get
{
fixed (char* name = &Name0)
{
return new string(name);
}
}
}
}
}
}
namespace FlaxEditor.Windows.Profiler
{
/// <summary>
@@ -204,7 +227,7 @@ namespace FlaxEditor.Windows.Profiler
{
var e = events[i];
if (e.Depth == 0 && new string(e.Name) == "Update")
if (e.Depth == 0 && e.Name == "Update")
{
return new ViewRange(ref e);
}
@@ -225,7 +248,7 @@ namespace FlaxEditor.Windows.Profiler
double scale = 100.0;
float x = (float)((e.Start - startTime) * scale);
float width = (float)(length * scale);
string name = new string(e.Name).Replace("::", ".");
string name = e.Name.Replace("::", ".");
var control = new Timeline.Event(x + xOffset, e.Depth + depthOffset, width)
{
@@ -399,7 +422,7 @@ namespace FlaxEditor.Windows.Profiler
subEventsMemoryTotal += sub.ManagedMemoryAllocation + e.NativeMemoryAllocation;
}
string name = new string(e.Name).Replace("::", ".");
string name = e.Name.Replace("::", ".");
var row = new Row
{

View File

@@ -2,7 +2,9 @@
#include "SplashScreen.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Render2D/FontAsset.h"
#include "Engine/Render2D/Font.h"
#include "Engine/Render2D/TextLayoutOptions.h"
@@ -120,6 +122,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()

View File

@@ -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>

View File

@@ -10,6 +10,7 @@
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/MException.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
struct InternalInitData
@@ -144,6 +145,17 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
box->Cache = value;
}
bool AnimGraph::IsReady() const
{
return BaseModel && BaseModel->IsLoaded();
}
bool AnimGraph::CanUseWithSkeleton(SkinnedModel* other) const
{
// All data loaded and bones count the same
return IsReady() && other && other->IsLoaded() && other->Skeleton.Bones.Count() == BaseModel->Skeleton.Bones.Count();
}
void AnimGraph::ClearCustomNode(Node* node)
{
// Clear data

View File

@@ -1,8 +1,10 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "AnimGraph.h"
#include "Engine/Engine/Time.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Graphics/Models/SkeletonData.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Engine/Time.h"
RootMotionData RootMotionData::Identity = { Vector3(0.0f), Quaternion(0.0f, 0.0f, 0.0f, 1.0f) };

View File

@@ -3,9 +3,9 @@
#pragma once
#include "Engine/Visject/VisjectGraph.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Content/Assets/Animation.h"
#include "Engine/Animations/AlphaBlend.h"
#include "Engine/Core/Math/Matrix.h"
#include "../Config.h"
#define ANIM_GRAPH_PARAM_BASE_MODEL_ID Guid(1000, 0, 0, 0)
@@ -21,6 +21,8 @@ class AnimSubGraph;
class AnimGraphBase;
class AnimGraphNode;
class AnimGraphExecutor;
class SkinnedModel;
class SkeletonData;
/// <summary>
/// The root motion data container. Supports displacement and rotation (no scale component).
@@ -777,22 +779,14 @@ public:
/// <summary>
/// Determines whether this graph is ready for the animation evaluation.
/// </summary>
/// <returns>True if is ready and can be used for the animation evaluation, otherwise false.</returns>
bool IsReady() const
{
return BaseModel && BaseModel->IsLoaded();
}
bool IsReady() const;
/// <summary>
/// Determines whether this graph can be used with the specified skeleton.
/// </summary>
/// <param name="other">The other skinned model to check.</param>
/// <returns>True if can perform the update, otherwise false.</returns>
bool CanUseWithSkeleton(SkinnedModel* other) const
{
// All data loaded and bones count the same
return IsReady() && other && other->IsLoaded() && other->Skeleton.Bones.Count() == BaseModel->Skeleton.Bones.Count();
}
bool CanUseWithSkeleton(SkinnedModel* other) const;
private:

View File

@@ -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;

View File

@@ -8,7 +8,7 @@
#include "Engine/Audio/AudioClip.h"
#include "Engine/Graphics/PostProcessSettings.h"
REGISTER_BINARY_ASSET(SceneAnimation, "FlaxEngine.SceneAnimation", nullptr, false);
REGISTER_BINARY_ASSET(SceneAnimation, "FlaxEngine.SceneAnimation", false);
SceneAnimation::SceneAnimation(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)

View File

@@ -9,6 +9,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Audio/AudioClip.h"
#include "Engine/Audio/AudioSource.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h"

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Level/Actor.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Level/Actors/PostFxVolume.h"
#include "SceneAnimation.h"

View File

@@ -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);
}

View File

@@ -4,6 +4,7 @@
#include "Audio.h"
#include "AudioSource.h"
#include "AudioBackend.h"
#include "Engine/Core/Log.h"
#include "Engine/Content/Upgraders/AudioClipUpgrader.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
@@ -12,7 +13,7 @@
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/AudioTool.h"
REGISTER_BINARY_ASSET(AudioClip, "FlaxEngine.AudioClip", ::New<AudioClipUpgrader>(), false);
REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClipUpgrader, false);
bool AudioClip::StreamingTask::Run()
{

View File

@@ -18,7 +18,7 @@
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#define FLAX_POS_TO_OAL(vec) -vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f
#define FLAX_POS_TO_OAL(vec) -vec.X * 0.01f, vec.Y * 0.01f, -vec.Z * 0.01f
namespace ALC
{

View File

@@ -26,7 +26,7 @@
#define MAX_OUTPUT_CHANNELS 8
#define MAX_CHANNELS_MATRIX_SIZE (MAX_INPUT_CHANNELS*MAX_OUTPUT_CHANNELS)
#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(-vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f)
#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f)
#define FLAX_VEC_TO_XAUDIO(vec) (*((X3DAUDIO_VECTOR*)&vec))
namespace XAudio2

View File

@@ -7,6 +7,7 @@
#include "Engine/Level/SceneQuery.h"
#include "Engine/Level/Actor.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Graphics/Models/ModelData.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h"

View File

@@ -2,6 +2,8 @@
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
namespace CSG
{
/// <summary>

View File

@@ -38,9 +38,10 @@ void Asset::OnDeleteObject()
if (!IsInternalType())
Content::AssetDisposing(this);
// Cache data
const bool wasMarkedToDelete = _deleteFileOnUnload != 0;
#if USE_EDITOR
const String path = wasMarkedToDelete ? GetPath() : String::Empty;
#endif
const Guid id = GetID();
// Fire unload event (every object referencing this asset or it's data should release reference so later actions are safe)
@@ -66,7 +67,7 @@ void Asset::OnDeleteObject()
// Base (after it `this` is invalid)
ManagedScriptingObject::OnDeleteObject();
// Check if asset was marked to delete
#if USE_EDITOR
if (wasMarkedToDelete)
{
LOG(Info, "Deleting asset '{0}':{1}.", path, id.ToString());
@@ -77,6 +78,7 @@ void Asset::OnDeleteObject()
// Delete file
Content::deleteFileSafety(path, id);
}
#endif
}
void Asset::CreateManaged()
@@ -131,6 +133,20 @@ void Asset::ChangeID(const Guid& newId)
CRASH;
}
bool Asset::LastLoadFailed() const
{
return _loadFailed != 0;
}
#if USE_EDITOR
bool Asset::ShouldDeleteFileOnUnload() const
{
return _deleteFileOnUnload != 0;
}
#endif
void Asset::Reload()
{
// It's better to call it from the main thread
@@ -225,7 +241,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;
@@ -335,13 +351,21 @@ void Asset::startLoading()
bool Asset::onLoad(LoadAssetTask* task)
{
// It may fail when task is cancelled and new one is created later (don't crash but just end with an error)
if (task->GetAsset() != this || _loadingTask == nullptr)
if (task->Asset.Get() != this || _loadingTask == nullptr)
return true;
Locker.Lock();
// Load asset
const LoadResult result = loadAsset();
LoadResult result;
{
#if TRACY_ENABLE
ZoneScoped;
const StringView name(GetPath());
ZoneName(*name, name.Length());
#endif
result = loadAsset();
}
const bool isLoaded = result == LoadResult::Ok;
const bool failed = !isLoaded;
_loadFailed = failed;

View File

@@ -82,7 +82,6 @@ public:
/// <summary>
/// Gets asset's reference count. Asset will be automatically unloaded when this reaches zero.
/// </summary>
/// <returns>The amount of references to that asset.</returns>
API_PROPERTY() int32 GetReferencesCount() const
{
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
@@ -107,21 +106,18 @@ public:
public:
/// <summary>
/// Gets the path to the asset storage.
/// Gets the path to the asset storage file. In Editor it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers.
/// </summary>
/// <returns>The asset file.</returns>
API_PROPERTY() virtual const String& GetPath() const = 0;
/// <summary>
/// Gets the asset type name.
/// </summary>
/// <returns>The typename.</returns>
virtual const String& GetTypeName() const = 0;
/// <summary>
/// Returns true if asset is loaded, otherwise false.
/// </summary>
/// <returns><c>true</c> if this asset is loaded; otherwise, <c>false</c>.</returns>
API_PROPERTY() FORCE_INLINE bool IsLoaded() const
{
return _isLoaded != 0;
@@ -130,16 +126,11 @@ public:
/// <summary>
/// Returns true if last asset loading failed, otherwise false.
/// </summary>
/// <returns><c>true</c> if last asset loading failed; otherwise, <c>false</c>.</returns>
API_PROPERTY() FORCE_INLINE bool LastLoadFailed() const
{
return _loadFailed != 0;
}
API_PROPERTY() bool LastLoadFailed() const;
/// <summary>
/// Determines whether this asset is virtual (generated or temporary, has no storage so it won't be saved).
/// </summary>
/// <returns><c>true</c> if this asset is virtual; otherwise, <c>false</c>.</returns>
API_PROPERTY() FORCE_INLINE bool IsVirtual() const
{
return _isVirtual != 0;
@@ -150,11 +141,7 @@ public:
/// <summary>
/// Determines whether this asset was marked to be deleted on unload.
/// </summary>
/// <returns><c>true</c> if this asset file was marked to be deleted on asset unload; otherwise, <c>false</c>.</returns>
API_PROPERTY() FORCE_INLINE bool ShouldDeleteFileOnUnload() const
{
return _deleteFileOnUnload != 0;
}
API_PROPERTY() bool ShouldDeleteFileOnUnload() const;
#endif

View File

@@ -274,3 +274,9 @@ public:
OnSet(asset);
}
};
template<typename T>
uint32 GetHash(const AssetReference<T>& key)
{
return GetHash(key.GetID());
}

View File

@@ -11,7 +11,7 @@
#include "Engine/Serialization/MemoryWriteStream.h"
#endif
REGISTER_BINARY_ASSET(Animation, "FlaxEngine.Animation", nullptr, false);
REGISTER_BINARY_ASSET(Animation, "FlaxEngine.Animation", false);
Animation::Animation(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)

Some files were not shown because too many files have changed in this diff Show More