19 Commits

Author SHA1 Message Date
578287f3bf _baseline emscripten build
Some checks are pending
Build Android / Game (Android, Release ARM64) (push) Waiting to run
Build iOS / Game (iOS, Release ARM64) (push) Waiting to run
Build Linux / Editor (Linux, Development x64) (push) Waiting to run
Build Linux / Game (Linux, Release x64) (push) Waiting to run
Build macOS / Editor (Mac, Development ARM64) (push) Waiting to run
Build macOS / Game (Mac, Release ARM64) (push) Waiting to run
Build Windows / Editor (Windows, Development x64) (push) Waiting to run
Build Windows / Game (Windows, Release x64) (push) Waiting to run
Cooker / Cook (Mac) (push) Waiting to run
Tests / Tests (Linux) (push) Waiting to run
Tests / Tests (Windows) (push) Waiting to run
2026-02-16 21:27:38 +02:00
Wojtek Figat
489c4a3661 Add packaging game files and bundling them into final Web app 2026-02-16 17:41:43 +01:00
Wojtek Figat
43dca143fa Add env vars impl to Web platform 2026-02-16 16:11:12 +01:00
Wojtek Figat
6bd13feba6 Add various fixes 2026-02-16 16:11:00 +01:00
Wojtek Figat
d1557e5292 Add support for running engine with a single-thread only (content, jobs, drawing, physics) 2026-02-16 16:10:31 +01:00
Wojtek Figat
6f13a33be2 Add LTO and Sanitizers support to Web build 2026-02-16 12:00:24 +01:00
Wojtek Figat
4ccf969f7a Add various fixes for Web 2026-02-16 11:59:44 +01:00
Wojtek Figat
3d206e06d0 Fix ThreadLocal when thread count limit is low on a target platform or if it's only main-thread 2026-02-16 11:59:12 +01:00
Wojtek Figat
25e90deed6 Fix Web (and consoles) to run without exceptions throw/catch support 2026-02-16 11:50:52 +01:00
Wojtek Figat
7bc687194f Fix Web to run without dotnet 2026-02-16 11:49:34 +01:00
Wojtek Figat
8a760dc1e2 Fix typo 2026-02-14 00:07:41 +01:00
Wojtek Figat
fc0b885a14 Fix missing Length in Vector4 2026-02-14 00:07:34 +01:00
Wojtek Figat
f12ad5c874 Add **Web platform with Emscripten** 2026-02-14 00:07:21 +01:00
Wojtek Figat
fd0584b406 Fix build regression from 865a26cbbe 2026-02-12 22:03:13 +01:00
Wojtek Figat
374f6e40bb Merge remote-tracking branch 'origin/1.12' into 1.12 2026-02-12 20:26:54 +01:00
Wojtek Figat
9c9e17a9d9 Fix warning when building C# lib with newer .NET than base version used on deps 2026-02-12 18:26:43 +01:00
Wojtek Figat
865a26cbbe Remove deprecated CommonValue
Deprecated on 31th July 2020
2026-02-12 18:26:08 +01:00
Wojtek Figat
56e825cad4 Build SDL for macOS (x64 and arm64) 2026-02-12 17:23:38 +01:00
Wojtek Figat
1df608e902 Remove deprecated NetworkDriverType 2026-02-12 16:43:01 +01:00
146 changed files with 2130 additions and 4690 deletions

View File

@@ -1,4 +1,4 @@
# Redirect to our own Git LFS server # Redirect to our own Git LFS server
[lfs] [lfs]
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs" #url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
locksverify = false locksverify = false

Binary file not shown.

View File

@@ -12,6 +12,6 @@ cd "`dirname "$0"`"
bash ./Development/Scripts/Mac/CallBuildTool.sh --genproject "$@" bash ./Development/Scripts/Mac/CallBuildTool.sh --genproject "$@"
# Build bindings for all editor configurations # Build bindings for all editor configurations
#echo Building C# bindings... echo Building C# bindings...
# TODO: Detect the correct architecture here # TODO: Detect the correct architecture here
#Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor

View File

@@ -141,6 +141,11 @@ API_ENUM() enum class BuildPlatform
/// </summary> /// </summary>
API_ENUM(Attributes="EditorDisplay(null, \"Windows ARM64\")") API_ENUM(Attributes="EditorDisplay(null, \"Windows ARM64\")")
WindowsARM64 = 15, WindowsARM64 = 15,
/// <summary>
/// Web
/// </summary>
Web = 16,
}; };
/// <summary> /// <summary>
@@ -188,6 +193,11 @@ enum class DotNetAOTModes
/// Use Mono AOT to cross-compile all used C# assemblies into native platform static libraries which can be linked into a single shared library. /// Use Mono AOT to cross-compile all used C# assemblies into native platform static libraries which can be linked into a single shared library.
/// </summary> /// </summary>
MonoAOTStatic, MonoAOTStatic,
/// <summary>
/// Target platform doesn't support .NET or it has been disabled.
/// </summary>
NoDotnet,
}; };
extern FLAXENGINE_API const Char* ToString(const BuildPlatform platform); extern FLAXENGINE_API const Char* ToString(const BuildPlatform platform);

View File

@@ -69,6 +69,10 @@
#include "Platform/iOS/iOSPlatformTools.h" #include "Platform/iOS/iOSPlatformTools.h"
#include "Engine/Platform/iOS/iOSPlatformSettings.h" #include "Engine/Platform/iOS/iOSPlatformSettings.h"
#endif #endif
#if PLATFORM_TOOLS_WEB
#include "Platform/Web/WebPlatformTools.h"
#include "Engine/Platform/Web/WebPlatformSettings.h"
#endif
namespace GameCookerImpl namespace GameCookerImpl
{ {
@@ -151,6 +155,8 @@ const Char* ToString(const BuildPlatform platform)
return TEXT("iOS ARM64"); return TEXT("iOS ARM64");
case BuildPlatform::WindowsARM64: case BuildPlatform::WindowsARM64:
return TEXT("Windows ARM64"); return TEXT("Windows ARM64");
case BuildPlatform::Web:
return TEXT("Web");
default: default:
return TEXT(""); return TEXT("");
} }
@@ -183,6 +189,8 @@ const Char* ToString(const DotNetAOTModes mode)
return TEXT("MonoAOTDynamic"); return TEXT("MonoAOTDynamic");
case DotNetAOTModes::MonoAOTStatic: case DotNetAOTModes::MonoAOTStatic:
return TEXT("MonoAOTStatic"); return TEXT("MonoAOTStatic");
case DotNetAOTModes::NoDotnet:
return TEXT("NoDotnet");
default: default:
return TEXT(""); return TEXT("");
} }
@@ -307,6 +315,10 @@ void CookingData::GetBuildPlatformName(const Char*& platform, const Char*& archi
platform = TEXT("Windows"); platform = TEXT("Windows");
architecture = TEXT("ARM64"); architecture = TEXT("ARM64");
break; break;
case BuildPlatform::Web:
platform = TEXT("Web");
architecture = TEXT("x86");
break;
default: default:
LOG(Fatal, "Unknown or unsupported build platform."); LOG(Fatal, "Unknown or unsupported build platform.");
} }
@@ -461,6 +473,11 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform)
case BuildPlatform::iOSARM64: case BuildPlatform::iOSARM64:
result = New<iOSPlatformTools>(); result = New<iOSPlatformTools>();
break; break;
#endif
#if PLATFORM_TOOLS_WEB
case BuildPlatform::Web:
result = New<WebPlatformTools>();
break;
#endif #endif
} }
Tools.Add(platform, result); Tools.Add(platform, result);
@@ -604,6 +621,9 @@ void GameCooker::GetCurrentPlatform(PlatformType& platform, BuildPlatform& build
case PlatformType::iOS: case PlatformType::iOS:
buildPlatform = BuildPlatform::iOSARM64; buildPlatform = BuildPlatform::iOSARM64;
break; break;
case PlatformType::Web:
buildPlatform = BuildPlatform::Web;
break;
default: ; default: ;
} }
} }

View File

@@ -106,6 +106,7 @@ namespace FlaxEditor
case BuildPlatform.MacOSARM64: case BuildPlatform.MacOSARM64:
case BuildPlatform.MacOSx64: return PlatformType.Mac; case BuildPlatform.MacOSx64: return PlatformType.Mac;
case BuildPlatform.iOSARM64: return PlatformType.iOS; case BuildPlatform.iOSARM64: return PlatformType.iOS;
case BuildPlatform.Web: return PlatformType.Web;
default: throw new ArgumentOutOfRangeException(nameof(buildPlatform), buildPlatform, null); default: throw new ArgumentOutOfRangeException(nameof(buildPlatform), buildPlatform, null);
} }
} }

View File

@@ -138,6 +138,7 @@ Array<byte> AndroidPlatformTools::SaveCache(CookingData& data, IBuildCache* cach
result.Add((const byte*)&platformCache, sizeof(platformCache)); result.Add((const byte*)&platformCache, sizeof(platformCache));
return result; return result;
} }
void AndroidPlatformTools::OnBuildStarted(CookingData& data) void AndroidPlatformTools::OnBuildStarted(CookingData& data)
{ {
// Adjust the cooking output folder to be located inside the Gradle assets directory // Adjust the cooking output folder to be located inside the Gradle assets directory
@@ -412,7 +413,6 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
} }
LOG(Info, "Output Android APK application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024); LOG(Info, "Output Android APK application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024);
return false; return false;
} }

View File

@@ -0,0 +1,157 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#if PLATFORM_TOOLS_WEB
#include "WebPlatformTools.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/CreateProcessSettings.h"
#include "Engine/Platform/Web/WebPlatformSettings.h"
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Core/Config/BuildSettings.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/JsonAsset.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
#include "Editor/Cooker/GameCooker.h"
IMPLEMENT_SETTINGS_GETTER(WebPlatformSettings, WebPlatform);
const Char* WebPlatformTools::GetDisplayName() const
{
return TEXT("Web");
}
const Char* WebPlatformTools::GetName() const
{
return TEXT("Web");
}
PlatformType WebPlatformTools::GetPlatform() const
{
return PlatformType::Web;
}
ArchitectureType WebPlatformTools::GetArchitecture() const
{
return ArchitectureType::x86;
}
DotNetAOTModes WebPlatformTools::UseAOT() const
{
return DotNetAOTModes::NoDotnet;
}
PixelFormat WebPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format)
{
// TODO: texture compression for Web (eg. ASTC for mobile and BC for others?)
return PixelFormatExtensions::FindUncompressedFormat(format);
}
bool WebPlatformTools::IsNativeCodeFile(CookingData& data, const String& file)
{
String extension = FileSystem::GetExtension(file);
return extension.IsEmpty() || extension == TEXT("html") || extension == TEXT("js") || extension == TEXT("wasm");
}
void WebPlatformTools::OnBuildStarted(CookingData& data)
{
// Adjust the cooking output folder for the data files so file_packager tool can build the and output final data inside the cooker output folder
data.DataOutputPath = data.CacheDirectory / TEXT("Files");
}
bool WebPlatformTools::OnPostProcess(CookingData& data)
{
const auto gameSettings = GameSettings::Get();
const auto platformSettings = WebPlatformSettings::Get();
const auto platformDataPath = data.GetPlatformBinariesRoot();
// Get name of the output binary (JavaScript and WebAssembly files match)
String gameJs;
{
Array<String> files;
FileSystem::DirectoryGetFiles(files, data.OriginalOutputPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
for (String& file : files)
{
if (file.EndsWith(TEXT(".js")))
{
String outputWasm = String(StringUtils::GetPathWithoutExtension(file)) + TEXT(".wasm");
if (files.Contains(outputWasm))
{
gameJs = file;
break;
}
}
}
}
if (gameJs.IsEmpty())
{
data.Error(TEXT("Failed to find the main JavaScript for the output game"));
return true;
}
// Pack data files into a single file using Emscripten's file_packager tool
{
CreateProcessSettings procSettings;
String emscriptenSdk = TEXT("EMSDK");
Platform::GetEnvironmentVariable(emscriptenSdk, emscriptenSdk);
procSettings.FileName = emscriptenSdk / TEXT("upstream/emscripten/tools/file_packager");
#if PLATFORM_WIN32
procSettings.FileName += TEXT(".bat");
#endif
procSettings.Arguments = String::Format(TEXT("files.data --preload \"{}@/\" --lz4 --js-output=files.js"), data.DataOutputPath);
procSettings.WorkingDirectory = data.OriginalOutputPath;
const int32 result = Platform::CreateProcess(procSettings);
if (result != 0)
{
if (!FileSystem::FileExists(procSettings.FileName))
data.Error(TEXT("Missing file_packager.bat. Ensure Emscripten SDK installation is valid and 'EMSDK' environment variable points to it."));
data.Error(String::Format(TEXT("Failed to package project files (result code: {0}). See log for more info."), result));
return true;
}
}
// TODO: customizable HTML templates
// Insert packaged file system with game data
{
String gameJsText;
if (File::ReadAllText(gameJs, gameJsText))
{
data.Error(String::Format(TEXT("Failed to load file '{}'"), gameJs));
return true;
}
const String filesIncludeBegin = TEXT("// include: files.js");
const String filesIncludeEnd = TEXT("// end include: files.js");
if (!gameJsText.Contains(filesIncludeBegin))
{
// Insert generated files.js into the main game file after the minimum_runtime_check.js include
String fileJsText;
String fileJs = data.OriginalOutputPath / TEXT("files.js");
if (File::ReadAllText(fileJs, fileJsText))
{
data.Error(String::Format(TEXT("Failed to load file '{}'"), fileJs));
return true;
}
const String insertPrefixLocation = TEXT("// end include: minimum_runtime_check.js");
int32 location = gameJsText.Find(insertPrefixLocation);
CHECK_RETURN(location != -1, true);
location += insertPrefixLocation.Length() + 1;
fileJsText = filesIncludeBegin + TEXT("\n") + fileJsText + TEXT("\n") + filesIncludeEnd + TEXT("\n");
gameJsText.Insert(location, fileJsText);
File::WriteAllText(gameJs, gameJsText, Encoding::UTF8);
}
}
const auto buildSettings = BuildSettings::Get();
if (buildSettings->SkipPackaging)
return false;
GameCooker::PackageFiles();
// TODO: minify/compress output JS files (in Release builds)
LOG(Info, "Output website size: {0} MB", FileSystem::GetDirectorySize(data.OriginalOutputPath) / 1024 / 1024);
return false;
}
#endif

View File

@@ -0,0 +1,27 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_TOOLS_WEB
#include "../../PlatformTools.h"
/// <summary>
/// The Web platform support tools.
/// </summary>
class WebPlatformTools : public PlatformTools
{
public:
// [PlatformTools]
const Char* GetDisplayName() const override;
const Char* GetName() const override;
PlatformType GetPlatform() const override;
ArchitectureType GetArchitecture() const override;
DotNetAOTModes UseAOT() const override;
PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override;
bool IsNativeCodeFile(CookingData& data, const String& file) override;
void OnBuildStarted(CookingData& data) override;
bool OnPostProcess(CookingData& data) override;
};
#endif

View File

@@ -572,6 +572,14 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE); COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE);
break; break;
} }
#endif
#if PLATFORM_TOOLS_WEB
case BuildPlatform::Web:
{
const char* platformDefineName = "PLATFORM_WEB";
// TODO: compile shaders for WebGPU
break;
}
#endif #endif
default: default:
{ {

View File

@@ -39,17 +39,23 @@ bool DeployDataStep::Perform(CookingData& data)
} }
String dstDotnet = data.DataOutputPath / TEXT("Dotnet"); String dstDotnet = data.DataOutputPath / TEXT("Dotnet");
const DotNetAOTModes aotMode = data.Tools->UseAOT(); const DotNetAOTModes aotMode = data.Tools->UseAOT();
const bool usAOT = aotMode != DotNetAOTModes::None; const bool usAOT = aotMode != DotNetAOTModes::None && aotMode != DotNetAOTModes::NoDotnet;
if (usAOT) if (usAOT)
{ {
// Deploy Dotnet files into intermediate cooking directory for AOT // Deploy Dotnet files into intermediate cooking directory for AOT
FileSystem::DeleteDirectory(dstDotnet); FileSystem::DeleteDirectory(dstDotnet);
dstDotnet = data.ManagedCodeOutputPath; dstDotnet = data.ManagedCodeOutputPath;
} }
if (buildSettings.SkipDotnetPackaging && data.Tools->UseSystemDotnet()) if (aotMode == DotNetAOTModes::NoDotnet)
{
// No .NET
FileSystem::DeleteDirectory(dstDotnet);
}
else if (buildSettings.SkipDotnetPackaging && data.Tools->UseSystemDotnet())
{ {
// Use system-installed .NET Runtime // Use system-installed .NET Runtime
FileSystem::DeleteDirectory(dstDotnet); FileSystem::DeleteDirectory(dstDotnet);
LOG(Info, "Not using .NET Runtime");
} }
else else
{ {

View File

@@ -12,7 +12,9 @@
void PrecompileAssembliesStep::OnBuildStarted(CookingData& data) void PrecompileAssembliesStep::OnBuildStarted(CookingData& data)
{ {
const DotNetAOTModes aotMode = data.Tools->UseAOT(); const DotNetAOTModes aotMode = data.Tools->UseAOT();
if (aotMode == DotNetAOTModes::None || EnumHasAllFlags(data.Options, BuildOptions::NoCook)) if (aotMode == DotNetAOTModes::None ||
aotMode == DotNetAOTModes::NoDotnet ||
EnumHasAllFlags(data.Options, BuildOptions::NoCook))
return; return;
const auto& buildSettings = *BuildSettings::Get(); const auto& buildSettings = *BuildSettings::Get();
@@ -49,7 +51,8 @@ void PrecompileAssembliesStep::OnBuildStarted(CookingData& data)
bool PrecompileAssembliesStep::Perform(CookingData& data) bool PrecompileAssembliesStep::Perform(CookingData& data)
{ {
const DotNetAOTModes aotMode = data.Tools->UseAOT(); const DotNetAOTModes aotMode = data.Tools->UseAOT();
if (aotMode == DotNetAOTModes::None) if (aotMode == DotNetAOTModes::None ||
aotMode == DotNetAOTModes::NoDotnet)
return false; return false;
const auto& buildSettings = *BuildSettings::Get(); const auto& buildSettings = *BuildSettings::Get();
if (buildSettings.SkipDotnetPackaging && data.Tools->UseSystemDotnet()) if (buildSettings.SkipDotnetPackaging && data.Tools->UseSystemDotnet())

View File

@@ -87,6 +87,7 @@ public class Editor : EditorModule
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID"); AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID");
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "iOS", "PLATFORM_TOOLS_IOS"); AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "iOS", "PLATFORM_TOOLS_IOS");
} }
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Web", "PLATFORM_TOOLS_WEB");
// Visual Studio integration // Visual Studio integration
if (options.Platform.Target == TargetPlatform.Windows && Flax.Build.Platform.BuildTargetPlatform == TargetPlatform.Windows) if (options.Platform.Target == TargetPlatform.Windows && Flax.Build.Platform.BuildTargetPlatform == TargetPlatform.Windows)

View File

@@ -93,6 +93,7 @@ namespace FlaxEditor.GUI
new PlatformData(PlatformType.PS5, icons.PS5Icon128, "PlayStation 5"), new PlatformData(PlatformType.PS5, icons.PS5Icon128, "PlayStation 5"),
new PlatformData(PlatformType.Mac, icons.MacOSIcon128, "macOS"), new PlatformData(PlatformType.Mac, icons.MacOSIcon128, "macOS"),
new PlatformData(PlatformType.iOS, icons.IOSIcon128, "iOS"), new PlatformData(PlatformType.iOS, icons.IOSIcon128, "iOS"),
new PlatformData(PlatformType.Web, icons.Flax64, "Web"),
}; };
const float IconSize = 64.0f; const float IconSize = 64.0f;

View File

@@ -201,10 +201,9 @@ namespace FlaxEditor.GUI.Timeline
var idx = stream.ReadInt32(); var idx = stream.ReadInt32();
var id = stream.ReadGuid(); var id = stream.ReadGuid();
object value = null; object value = null;
if (version == 2) if (version <= 2)
stream.ReadCommonValue(ref value); throw new Exception("Not supported asset version. Open and re-save asset with Flax 1.11.");
else value = stream.ReadVariant();
value = stream.ReadVariant();
Emitters[idx].ParametersOverrides.Add(id, value); Emitters[idx].ParametersOverrides.Add(id, value);
} }

View File

@@ -1603,9 +1603,9 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Platform Switch", Title = "Platform Switch",
Description = "Gets the input value based on the runtime-platform type", Description = "Gets the input value based on the runtime-platform type",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
Size = new Float2(220, 240), Size = new Float2(220, 260),
ConnectionsHints = ConnectionsHint.Value, ConnectionsHints = ConnectionsHint.Value,
IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
DependentBoxes = new[] { 0 }, DependentBoxes = new[] { 0 },
Elements = new[] Elements = new[]
{ {
@@ -1622,6 +1622,7 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Input(9, "PlayStation 5", true, null, 10), NodeElementArchetype.Factory.Input(9, "PlayStation 5", true, null, 10),
NodeElementArchetype.Factory.Input(10, "Mac", true, null, 11), NodeElementArchetype.Factory.Input(10, "Mac", true, null, 11),
NodeElementArchetype.Factory.Input(11, "iOS", true, null, 12), NodeElementArchetype.Factory.Input(11, "iOS", true, null, 12),
NodeElementArchetype.Factory.Input(12, "Web", true, null, 13),
} }
}, },
new NodeArchetype new NodeArchetype

View File

@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements; using FlaxEditor.Surface.Elements;
using FlaxEditor.Utilities; using FlaxEditor.Utilities;
using FlaxEngine; using FlaxEngine;
@@ -503,205 +502,8 @@ namespace FlaxEditor.Surface
var tmpHints = _cachedConnections.Value; var tmpHints = _cachedConnections.Value;
var guidBytes = new byte[16]; var guidBytes = new byte[16];
if (version < 7000) if (version < 7000)
{ throw new Exception("Not supported Visject Surface version. Open and re-save asset with Flax 1.11.");
// Time saved (not used anymore to prevent binary diffs after saving unmodified surface) if (version == 7000)
stream.ReadInt64();
// Nodes count
int nodesCount = stream.ReadInt32();
if (Nodes.Capacity < nodesCount)
Nodes.Capacity = nodesCount;
tmpHints.Clear();
tmpHints.Capacity = Mathf.Max(tmpHints.Capacity, nodesCount * 4);
// Parameters count
int parametersCount = stream.ReadInt32();
if (Parameters.Capacity < parametersCount)
Parameters.Capacity = parametersCount;
// For each node
for (int i = 0; i < nodesCount; i++)
{
// ID
uint id = stream.ReadUInt32();
// Type
ushort typeId = stream.ReadUInt16();
ushort groupId = stream.ReadUInt16();
// Create node
SurfaceNode node;
if (groupId == Archetypes.Custom.GroupID)
node = new DummyCustomNode(id, this);
else
node = NodeFactory.CreateNode(nodeArchetypes, id, this, groupId, typeId);
if (node == null)
node = new MissingNode(id, this, groupId, typeId);
Nodes.Add(node);
}
// For each param
for (int i = 0; i < parametersCount; i++)
{
// Create param
var param = new SurfaceParameter();
Parameters.Add(param);
// Properties
param.Type = new ScriptType(GetGraphParameterValueType((GraphParamType_Deprecated)stream.ReadByte()));
stream.Read(guidBytes, 0, 16);
param.ID = new Guid(guidBytes);
param.Name = stream.ReadStr(97);
param.IsPublic = stream.ReadByte() != 0;
bool isStatic = stream.ReadByte() != 0;
bool isUIVisible = stream.ReadByte() != 0;
bool isUIEditable = stream.ReadByte() != 0;
// References [Deprecated]
int refsCount = stream.ReadInt32();
for (int j = 0; j < refsCount; j++)
{
uint refID = stream.ReadUInt32();
}
// Value
stream.ReadCommonValue(ref param.Value);
// Meta
param.Meta.Load(stream);
}
// For each node
for (int i = 0; i < nodesCount; i++)
{
var node = Nodes[i];
int valuesCnt = stream.ReadInt32();
int firstValueReadIdx = 0;
// Special case for missing nodes
if (node is DummyCustomNode customNode)
{
node = null;
// Values check
if (valuesCnt < 2)
throw new Exception("Missing custom nodes data.");
// Node typename check
object typeNameValue = null;
stream.ReadCommonValue(ref typeNameValue);
firstValueReadIdx = 1;
string typeName = typeNameValue as string ?? string.Empty;
// Find custom node archetype that matches this node type (it must be unique)
if (customNodes?.Archetypes != null && typeName.Length != 0)
{
NodeArchetype arch = null;
foreach (var nodeArchetype in customNodes.Archetypes)
{
if (string.Equals(Archetypes.Custom.GetNodeTypeName(nodeArchetype), typeName, StringComparison.OrdinalIgnoreCase))
{
arch = nodeArchetype;
break;
}
}
if (arch != null)
node = NodeFactory.CreateNode(customNode.ID, this, customNodes, arch);
}
// Fallback to the
if (node == null)
{
Editor.LogWarning(string.Format("Cannot find custom node archetype for {0}", typeName));
node = new MissingNode(customNode.ID, this, Archetypes.Custom.GroupID, customNode.Archetype.TypeID);
}
Nodes[i] = node;
// Store node typename in values container
node.Values[0] = typeName;
}
if (node is MissingNode)
{
// Read all values
Array.Resize(ref node.Values, valuesCnt);
for (int j = firstValueReadIdx; j < valuesCnt; j++)
{
// ReSharper disable once PossibleNullReferenceException
stream.ReadCommonValue(ref node.Values[j]);
}
firstValueReadIdx = valuesCnt = node.Values.Length;
}
// Values
int nodeValuesCnt = node.Values?.Length ?? 0;
if (valuesCnt == nodeValuesCnt)
{
for (int j = firstValueReadIdx; j < valuesCnt; j++)
{
// ReSharper disable once PossibleNullReferenceException
stream.ReadCommonValue(ref node.Values[j]);
}
}
else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0)
{
node.Values = new object[valuesCnt];
for (int j = firstValueReadIdx; j < valuesCnt; j++)
{
// ReSharper disable once PossibleNullReferenceException
stream.ReadCommonValue(ref node.Values[j]);
}
}
else
{
Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID));
object dummy = null;
for (int j = firstValueReadIdx; j < valuesCnt; j++)
{
stream.ReadCommonValue(ref dummy);
if (j < nodeValuesCnt &&
dummy != null &&
node.Values[j] != null &&
node.Values[j].GetType() == dummy.GetType())
{
node.Values[j] = dummy;
}
}
}
// Boxes
ushort boxesCount = stream.ReadUInt16();
for (int j = 0; j < boxesCount; j++)
{
var id = stream.ReadByte();
stream.ReadUInt32(); // Skip type
ushort connectionsCnt = stream.ReadUInt16();
ConnectionHint hint;
hint.NodeB = node.ID;
hint.BoxB = id;
for (int k = 0; k < connectionsCnt; k++)
{
uint targetNodeID = stream.ReadUInt32();
byte targetBoxID = stream.ReadByte();
hint.NodeA = targetNodeID;
hint.BoxA = targetBoxID;
tmpHints.Add(hint);
}
}
// Meta
node.Meta.Load(stream);
OnControlLoaded(node, SurfaceNodeActions.Load);
}
}
else if (version == 7000)
{ {
// Nodes count // Nodes count
int nodesCount = stream.ReadInt32(); int nodesCount = stream.ReadInt32();

View File

@@ -667,310 +667,6 @@ namespace FlaxEditor.Utilities
stream.Write(value.Size.Y); stream.Write(value.Size.Y);
} }
internal static void ReadCommonValue(this BinaryReader stream, ref object value)
{
byte type = stream.ReadByte();
switch (type)
{
case 0: // CommonType::Bool:
value = stream.ReadByte() != 0;
break;
case 1: // CommonType::Integer:
{
value = stream.ReadInt32();
}
break;
case 2: // CommonType::Float:
{
value = stream.ReadSingle();
}
break;
case 3: // CommonType::Vector2:
{
value = stream.ReadFloat2();
}
break;
case 4: // CommonType::Vector3:
{
value = stream.ReadFloat3();
}
break;
case 5: // CommonType::Vector4:
{
value = stream.ReadFloat4();
}
break;
case 6: // CommonType::Color:
{
value = stream.ReadColor();
}
break;
case 7: // CommonType::Guid:
{
value = stream.ReadGuid();
}
break;
case 8: // CommonType::String:
{
int length = stream.ReadInt32();
if (length <= 0)
{
value = string.Empty;
}
else
{
var data = new char[length];
for (int i = 0; i < length; i++)
{
var c = stream.ReadUInt16();
data[i] = (char)(c ^ 953);
}
value = new string(data);
}
break;
}
case 9: // CommonType::Box:
{
value = new BoundingBox(new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()),
new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()));
}
break;
case 10: // CommonType::Rotation:
{
value = stream.ReadQuaternion();
}
break;
case 11: // CommonType::Transform:
{
value = new Transform(new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()),
new Quaternion(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()),
new Float3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()));
}
break;
case 12: // CommonType::Sphere:
{
value = new BoundingSphere(new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()),
stream.ReadSingle());
}
break;
case 13: // CommonType::Rect:
{
value = new Rectangle(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle());
}
break;
case 15: // CommonType::Matrix
{
value = new Matrix(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(),
stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(),
stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(),
stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle());
break;
}
case 16: // CommonType::Blob
{
int length = stream.ReadInt32();
value = stream.ReadBytes(length);
break;
}
case 18: // CommonType::Ray
{
value = new Ray(new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()),
new Vector3(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()));
break;
}
case 19: // CommonType::Int2
{
value = stream.ReadInt2();
break;
}
case 20: // CommonType::Int3
{
value = stream.ReadInt3();
break;
}
case 21: // CommonType::Int4
{
value = stream.ReadInt4();
break;
}
default: throw new SystemException();
}
}
internal static void WriteCommonValue(this BinaryWriter stream, object value)
{
if (value is bool asBool)
{
stream.Write((byte)0);
stream.Write((byte)(asBool ? 1 : 0));
}
else if (value is int asInt)
{
stream.Write((byte)1);
stream.Write(asInt);
}
else if (value is long asLong)
{
stream.Write((byte)1);
stream.Write((int)asLong);
}
else if (value is float asFloat)
{
stream.Write((byte)2);
stream.Write(asFloat);
}
else if (value is double asDouble)
{
stream.Write((byte)2);
stream.Write((float)asDouble);
}
else if (value is Vector2 asVector2)
{
stream.Write((byte)3);
stream.Write((float)asVector2.X);
stream.Write((float)asVector2.Y);
}
else if (value is Vector3 asVector3)
{
stream.Write((byte)4);
stream.Write((float)asVector3.X);
stream.Write((float)asVector3.Y);
stream.Write((float)asVector3.Z);
}
else if (value is Vector4 asVector4)
{
stream.Write((byte)5);
stream.Write((float)asVector4.X);
stream.Write((float)asVector4.Y);
stream.Write((float)asVector4.Z);
stream.Write((float)asVector4.W);
}
else if (value is Color asColor)
{
stream.Write((byte)6);
stream.Write(asColor.R);
stream.Write(asColor.G);
stream.Write(asColor.B);
stream.Write(asColor.A);
}
else if (value is Guid asGuid)
{
stream.Write((byte)7);
stream.WriteGuid(ref asGuid);
}
else if (value is string asString)
{
stream.Write((byte)8);
stream.Write(asString.Length);
for (int i = 0; i < asString.Length; i++)
stream.Write((ushort)(asString[i] ^ -14));
}
else if (value is BoundingBox asBox)
{
stream.Write((byte)9);
stream.Write((float)asBox.Minimum.X);
stream.Write((float)asBox.Minimum.Y);
stream.Write((float)asBox.Minimum.Z);
stream.Write((float)asBox.Maximum.X);
stream.Write((float)asBox.Maximum.Y);
stream.Write((float)asBox.Maximum.Z);
}
else if (value is Quaternion asRotation)
{
stream.Write((byte)10);
stream.Write(asRotation.X);
stream.Write(asRotation.Y);
stream.Write(asRotation.Z);
stream.Write(asRotation.X);
}
else if (value is Transform asTransform)
{
stream.Write((byte)11);
stream.Write((float)asTransform.Translation.X);
stream.Write((float)asTransform.Translation.Y);
stream.Write((float)asTransform.Translation.Z);
stream.Write(asTransform.Orientation.X);
stream.Write(asTransform.Orientation.Y);
stream.Write(asTransform.Orientation.Z);
stream.Write(asTransform.Orientation.X);
stream.Write(asTransform.Scale.X);
stream.Write(asTransform.Scale.Y);
stream.Write(asTransform.Scale.Z);
}
else if (value is BoundingSphere asSphere)
{
stream.Write((byte)12);
stream.Write((float)asSphere.Center.X);
stream.Write((float)asSphere.Center.Y);
stream.Write((float)asSphere.Center.Z);
stream.Write((float)asSphere.Radius);
}
else if (value is Rectangle asRect)
{
stream.Write((byte)13);
stream.Write(asRect.Location.X);
stream.Write(asRect.Location.Y);
stream.Write(asRect.Size.X);
stream.Write(asRect.Size.Y);
}
else if (value is Matrix asMatrix)
{
stream.Write((byte)15);
stream.Write(asMatrix.M11);
stream.Write(asMatrix.M12);
stream.Write(asMatrix.M13);
stream.Write(asMatrix.M14);
stream.Write(asMatrix.M21);
stream.Write(asMatrix.M22);
stream.Write(asMatrix.M23);
stream.Write(asMatrix.M24);
stream.Write(asMatrix.M31);
stream.Write(asMatrix.M32);
stream.Write(asMatrix.M33);
stream.Write(asMatrix.M34);
stream.Write(asMatrix.M41);
stream.Write(asMatrix.M42);
stream.Write(asMatrix.M43);
stream.Write(asMatrix.M44);
}
else if (value is byte[] asBlob)
{
stream.Write((byte)16);
stream.Write(asBlob.Length);
stream.Write(asBlob);
}
else if (value is Ray asRay)
{
stream.Write((byte)18);
stream.Write((float)asRay.Position.X);
stream.Write((float)asRay.Position.Y);
stream.Write((float)asRay.Position.Z);
stream.Write((float)asRay.Direction.X);
stream.Write((float)asRay.Direction.Y);
stream.Write((float)asRay.Direction.Z);
}
else if (value is Int2 asInt2)
{
stream.Write((byte)19);
stream.Write(asInt2);
}
else if (value is Int3 asInt3)
{
stream.Write((byte)20);
stream.Write(asInt3);
}
else if (value is Int4 asInt4)
{
stream.Write((byte)21);
stream.Write(asInt4);
}
else
{
throw new NotSupportedException(string.Format("Invalid Common Value type {0}", value != null ? value.GetType().ToString() : "null"));
}
}
/// <summary> /// <summary>
/// Shows the source code window. /// Shows the source code window.
/// </summary> /// </summary>

View File

@@ -52,6 +52,7 @@ namespace FlaxEditor.Windows
{ PlatformType.PS5, new PS5() }, { PlatformType.PS5, new PS5() },
{ PlatformType.Mac, new Mac() }, { PlatformType.Mac, new Mac() },
{ PlatformType.iOS, new iOS() }, { PlatformType.iOS, new iOS() },
{ PlatformType.Web, new Web() },
}; };
public BuildTabProxy(GameCookerWindow win, PlatformSelector platformSelector) public BuildTabProxy(GameCookerWindow win, PlatformSelector platformSelector)
@@ -165,6 +166,7 @@ namespace FlaxEditor.Windows
case BuildPlatform.UWPx64: case BuildPlatform.UWPx64:
case BuildPlatform.LinuxX64: case BuildPlatform.LinuxX64:
case BuildPlatform.AndroidARM64: case BuildPlatform.AndroidARM64:
case BuildPlatform.Web:
text += "\nUse Flax Launcher and download the required package."; text += "\nUse Flax Launcher and download the required package.";
break; break;
#endif #endif
@@ -538,6 +540,11 @@ namespace FlaxEditor.Windows
protected override BuildPlatform BuildPlatform => BuildPlatform.iOSARM64; protected override BuildPlatform BuildPlatform => BuildPlatform.iOSARM64;
} }
class Web : Platform
{
protected override BuildPlatform BuildPlatform => BuildPlatform.Web;
}
class Editor : CustomEditor class Editor : CustomEditor
{ {
private PlatformType _platform; private PlatformType _platform;
@@ -592,6 +599,9 @@ namespace FlaxEditor.Windows
case PlatformType.iOS: case PlatformType.iOS:
name = "iOS"; name = "iOS";
break; break;
case PlatformType.Web:
name = "Web";
break;
default: default:
name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); name = Utilities.Utils.GetPropertyNameUI(_platform.ToString());
break; break;

View File

@@ -60,6 +60,9 @@ public class Audio : EngineModule
case TargetPlatform.iOS: case TargetPlatform.iOS:
useOpenAL = true; useOpenAL = true;
break; break;
case TargetPlatform.Web:
// TODO: audio playback on Web (OpenAL)
break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);
} }

View File

@@ -194,6 +194,7 @@ bool AudioClip::ExtractData(Array<byte>& resultData, AudioDataInfo& resultDataIn
bool AudioClip::ExtractDataFloat(Array<float>& resultData, AudioDataInfo& resultDataInfo) bool AudioClip::ExtractDataFloat(Array<float>& resultData, AudioDataInfo& resultDataInfo)
{ {
#if COMPILE_WITH_AUDIO_TOOL
// Extract PCM data // Extract PCM data
Array<byte> data; Array<byte> data;
if (ExtractDataRaw(data, resultDataInfo)) if (ExtractDataRaw(data, resultDataInfo))
@@ -205,6 +206,9 @@ bool AudioClip::ExtractDataFloat(Array<float>& resultData, AudioDataInfo& result
resultDataInfo.BitDepth = 32; resultDataInfo.BitDepth = 32;
return false; return false;
#else
return true;
#endif
} }
bool AudioClip::ExtractDataRaw(Array<byte>& resultData, AudioDataInfo& resultDataInfo) bool AudioClip::ExtractDataRaw(Array<byte>& resultData, AudioDataInfo& resultDataInfo)
@@ -475,6 +479,7 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
} }
info.NumSamples = Math::AlignDown(data.Length() / bytesPerSample, info.NumChannels * bytesPerSample); info.NumSamples = Math::AlignDown(data.Length() / bytesPerSample, info.NumChannels * bytesPerSample);
#if COMPILE_WITH_AUDIO_TOOL
// Convert to Mono if used as 3D source and backend doesn't support it // Convert to Mono if used as 3D source and backend doesn't support it
if (Is3D() && info.NumChannels > 1 && EnumHasNoneFlags(AudioBackend::Features(), AudioBackend::FeatureFlags::SpatialMultiChannel)) if (Is3D() && info.NumChannels > 1 && EnumHasNoneFlags(AudioBackend::Features(), AudioBackend::FeatureFlags::SpatialMultiChannel))
{ {
@@ -486,6 +491,7 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
info.NumSamples = samplesPerChannel; info.NumSamples = samplesPerChannel;
data = Span<byte>(tmp2.Get(), tmp2.Count()); data = Span<byte>(tmp2.Get(), tmp2.Count());
} }
#endif
// Write samples to the audio buffer (create one if missing) // Write samples to the audio buffer (create one if missing)
Locker.Lock(); // StreamingTask loads buffers without lock so do it here Locker.Lock(); // StreamingTask loads buffers without lock so do it here

View File

@@ -3,6 +3,7 @@
#include "Material.h" #include "Material.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/Types/DataContainer.h" #include "Engine/Core/Types/DataContainer.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Content/Upgraders/ShaderAssetUpgrader.h" #include "Engine/Content/Upgraders/ShaderAssetUpgrader.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUDevice.h"

View File

@@ -80,10 +80,14 @@ namespace
// Loading assets // Loading assets
THREADLOCAL LoadingThread* ThisLoadThread = nullptr; THREADLOCAL LoadingThread* ThisLoadThread = nullptr;
LoadingThread* MainLoadThread = nullptr; LoadingThread* MainLoadThread = nullptr;
#if PLATFORM_THREADS_LIMIT > 1
Array<LoadingThread*> LoadThreads; Array<LoadingThread*> LoadThreads;
ConcurrentTaskQueue<ContentLoadTask> LoadTasks; ConcurrentTaskQueue<ContentLoadTask> LoadTasks;
ConditionVariable LoadTasksSignal; ConditionVariable LoadTasksSignal;
CriticalSection LoadTasksMutex; CriticalSection LoadTasksMutex;
#else
Array<ContentLoadTask*> LoadTasks;
#endif
// Unloading assets // Unloading assets
Dictionary<Asset*, TimeSpan> UnloadQueue; Dictionary<Asset*, TimeSpan> UnloadQueue;
@@ -125,11 +129,12 @@ bool ContentService::Init()
Cache.Init(); Cache.Init();
// Create loading threads // Create loading threads
MainLoadThread = New<LoadingThread>();
ThisLoadThread = MainLoadThread;
#if PLATFORM_THREADS_LIMIT > 1
const CPUInfo cpuInfo = Platform::GetCPUInfo(); const CPUInfo cpuInfo = Platform::GetCPUInfo();
const int32 count = Math::Clamp(Math::CeilToInt(LOADING_THREAD_PER_LOGICAL_CORE * (float)cpuInfo.LogicalProcessorCount), 1, 12); const int32 count = Math::Clamp(Math::CeilToInt(LOADING_THREAD_PER_LOGICAL_CORE * (float)cpuInfo.LogicalProcessorCount), 1, 12);
LOG(Info, "Creating {0} content loading threads...", count); LOG(Info, "Creating {0} content loading threads...", count);
MainLoadThread = New<LoadingThread>();
ThisLoadThread = MainLoadThread;
LoadThreads.Resize(count); LoadThreads.Resize(count);
for (int32 i = 0; i < count; i++) for (int32 i = 0; i < count; i++)
{ {
@@ -142,6 +147,7 @@ bool ContentService::Init()
return true; return true;
} }
} }
#endif
return false; return false;
} }
@@ -150,14 +156,29 @@ void ContentService::Update()
{ {
PROFILE_CPU(); PROFILE_CPU();
ScopeLock lock(LoadedAssetsToInvokeLocker); #if PLATFORM_THREADS_LIMIT == 1
// Run content-streaming tasks on a main thread
if (LoadTasks.HasItems())
{
double timeLimit = 0.01; // 10ms
double startTime = Platform::GetTimeSeconds();
do
{
auto task = LoadTasks[0];
LoadTasks.RemoveAt(0);
MainLoadThread->Run(task);
} while (LoadTasks.HasItems() && Platform::GetTimeSeconds() - startTime < timeLimit);
}
#endif
// Broadcast `OnLoaded` events // Broadcast `OnLoaded` events
LoadedAssetsToInvokeLocker.Lock();
while (LoadedAssetsToInvoke.HasItems()) while (LoadedAssetsToInvoke.HasItems())
{ {
auto asset = LoadedAssetsToInvoke.Dequeue(); auto asset = LoadedAssetsToInvoke.Dequeue();
asset->onLoaded_MainThread(); asset->onLoaded_MainThread();
} }
LoadedAssetsToInvokeLocker.Unlock();
} }
void ContentService::LateUpdate() void ContentService::LateUpdate()
@@ -219,10 +240,12 @@ void ContentService::LateUpdate()
void ContentService::BeforeExit() void ContentService::BeforeExit()
{ {
#if PLATFORM_THREADS_LIMIT > 1
// Signal threads to end work soon // Signal threads to end work soon
for (auto thread : LoadThreads) for (auto thread : LoadThreads)
thread->NotifyExit(); thread->NotifyExit();
LoadTasksSignal.NotifyAll(); LoadTasksSignal.NotifyAll();
#endif
} }
void ContentService::Dispose() void ContentService::Dispose()
@@ -251,6 +274,7 @@ void ContentService::Dispose()
// NOW dispose graphics device - where there is no loaded assets at all // NOW dispose graphics device - where there is no loaded assets at all
Graphics::DisposeDevice(); Graphics::DisposeDevice();
#if PLATFORM_THREADS_LIMIT > 1
// Exit all load threads // Exit all load threads
for (auto thread : LoadThreads) for (auto thread : LoadThreads)
thread->NotifyExit(); thread->NotifyExit();
@@ -258,12 +282,19 @@ void ContentService::Dispose()
for (auto thread : LoadThreads) for (auto thread : LoadThreads)
thread->Join(); thread->Join();
LoadThreads.ClearDelete(); LoadThreads.ClearDelete();
#endif
Delete(MainLoadThread); Delete(MainLoadThread);
MainLoadThread = nullptr; MainLoadThread = nullptr;
ThisLoadThread = nullptr; ThisLoadThread = nullptr;
#if PLATFORM_THREADS_LIMIT > 1
// Cancel all remaining tasks (no chance to execute them) // Cancel all remaining tasks (no chance to execute them)
LoadTasks.CancelAll(); LoadTasks.CancelAll();
#else
for (auto* e : LoadTasks)
e->Cancel();
LoadTasks.Clear();
#endif
} }
IAssetFactory::Collection& IAssetFactory::Get() IAssetFactory::Collection& IAssetFactory::Get()
@@ -329,6 +360,7 @@ String LoadingThread::ToString() const
int32 LoadingThread::Run() int32 LoadingThread::Run()
{ {
#if PLATFORM_THREADS_LIMIT > 1
PROFILE_MEM(Content); PROFILE_MEM(Content);
#if USE_EDITOR && PLATFORM_WINDOWS #if USE_EDITOR && PLATFORM_WINDOWS
// Initialize COM // Initialize COM
@@ -366,6 +398,7 @@ int32 LoadingThread::Run()
} }
ThisLoadThread = nullptr; ThisLoadThread = nullptr;
#endif
return 0; return 0;
} }
@@ -382,7 +415,9 @@ String ContentLoadTask::ToString() const
void ContentLoadTask::Enqueue() void ContentLoadTask::Enqueue()
{ {
LoadTasks.Add(this); LoadTasks.Add(this);
#if PLATFORM_THREADS_LIMIT > 1
LoadTasksSignal.NotifyOne(); LoadTasksSignal.NotifyOne();
#endif
} }
bool ContentLoadTask::Run() bool ContentLoadTask::Run()
@@ -1137,6 +1172,7 @@ void Content::WaitForTask(ContentLoadTask* loadingTask, double timeoutInMillisec
#define CHECK_CONDITIONS() (!Engine::ShouldExit() && (timeoutInSeconds <= 0.0 || Platform::GetTimeSeconds() - startTime < timeoutInSeconds)) #define CHECK_CONDITIONS() (!Engine::ShouldExit() && (timeoutInSeconds <= 0.0 || Platform::GetTimeSeconds() - startTime < timeoutInSeconds))
do do
{ {
#if PLATFORM_THREADS_LIMIT > 1
// Give opportunity for other threads to use the current core // Give opportunity for other threads to use the current core
if (loopCounter == 0) if (loopCounter == 0)
; // First run is fast ; // First run is fast
@@ -1183,6 +1219,34 @@ void Content::WaitForTask(ContentLoadTask* loadingTask, double timeoutInMillisec
LoadTasks.enqueue_bulk(localQueue.Get(), localQueue.Count()); LoadTasks.enqueue_bulk(localQueue.Get(), localQueue.Count());
localQueue.Clear(); localQueue.Clear();
} }
#else
// Try to execute content tasks
if (task->IsQueued() && CHECK_CONDITIONS() && !LoadTasks.Remove((ContentLoadTask*)task))
{
PROFILE_CPU_NAMED("Inline");
ZoneColor(0xffaaaaaa);
thread->Run((ContentLoadTask*)task);
}
while (!task->IsQueued() && CHECK_CONDITIONS() && LoadTasks.HasItems())
{
// Find a task that can be executed (some tasks may be waiting for other tasks to finish so they are not queued yet)
int32 index = 0;
for (int32 i = 0; i < LoadTasks.Count(); i++)
{
if (LoadTasks[i]->GetContinueWithTask() == task)
{
index = i;
break;
}
}
ContentLoadTask* tmp = LoadTasks[index];
LoadTasks.RemoveAt(index);
PROFILE_CPU_NAMED("Inline");
ZoneColor(0xffaaaaaa);
thread->Run(tmp);
}
#endif
// Check if task is done // Check if task is done
if (task->IsEnded()) if (task->IsEnded())

View File

@@ -5,14 +5,14 @@
#include "Engine/Threading/Task.h" #include "Engine/Threading/Task.h"
class Asset; class Asset;
class LoadingThread;
/// <summary> /// <summary>
/// Describes content loading task object. /// Describes content loading task object.
/// </summary> /// </summary>
class ContentLoadTask : public Task class ContentLoadTask : public Task
{ {
friend LoadingThread; friend class LoadingThread;
friend class ContentService;
public: public:
/// <summary> /// <summary>

View File

@@ -286,13 +286,13 @@ FlaxStorage::LockData FlaxStorage::LockSafe()
uint32 FlaxStorage::GetRefCount() const uint32 FlaxStorage::GetRefCount() const
{ {
return (uint32)Platform::AtomicRead((int64*)&_refCount); return (uint32)Platform::AtomicRead((intptr*)&_refCount);
} }
bool FlaxStorage::ShouldDispose() const bool FlaxStorage::ShouldDispose() const
{ {
return Platform::AtomicRead((int64*)&_refCount) == 0 && return Platform::AtomicRead((intptr*)&_refCount) == 0 &&
Platform::AtomicRead((int64*)&_chunksLock) == 0 && Platform::AtomicRead((intptr*)&_chunksLock) == 0 &&
Platform::GetTimeSeconds() - _lastRefLostTime >= 0.5; // TTL in seconds Platform::GetTimeSeconds() - _lastRefLostTime >= 0.5; // TTL in seconds
} }

View File

@@ -87,10 +87,10 @@ public:
protected: protected:
// State // State
int64 _refCount = 0; intptr _refCount = 0;
int64 _chunksLock = 0; intptr _chunksLock = 0;
int64 _files = 0; intptr _files = 0;
int64 _isUnloadingData = 0; intptr _isUnloadingData = 0;
double _lastRefLostTime; double _lastRefLostTime;
CriticalSection _loadLocker; CriticalSection _loadLocker;

View File

@@ -111,6 +111,10 @@
#define _DEPRECATED(_0, _1, LASTARG, ...) LASTARG #define _DEPRECATED(_0, _1, LASTARG, ...) LASTARG
#define DEPRECATED(...) _DEPRECATED(, ##__VA_ARGS__, _DEPRECATED_1(__VA_ARGS__), _DEPRECATED_0()) #define DEPRECATED(...) _DEPRECATED(, ##__VA_ARGS__, _DEPRECATED_1(__VA_ARGS__), _DEPRECATED_0())
#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
#define HAS_EXCEPTIONS 1
#endif
// C++ 17 // C++ 17
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#define IF_CONSTEXPR constexpr #define IF_CONSTEXPR constexpr

View File

@@ -95,6 +95,8 @@ IMPLEMENT_ENGINE_SETTINGS_GETTER(SwitchPlatformSettings, SwitchPlatform);
IMPLEMENT_ENGINE_SETTINGS_GETTER(MacPlatformSettings, MacPlatform); IMPLEMENT_ENGINE_SETTINGS_GETTER(MacPlatformSettings, MacPlatform);
#elif PLATFORM_IOS #elif PLATFORM_IOS
IMPLEMENT_ENGINE_SETTINGS_GETTER(iOSPlatformSettings, iOSPlatform); IMPLEMENT_ENGINE_SETTINGS_GETTER(iOSPlatformSettings, iOSPlatform);
#elif PLATFORM_WEB
IMPLEMENT_ENGINE_SETTINGS_GETTER(WebPlatformSettings, WebPlatform);
#else #else
#error Unknown platform #error Unknown platform
#endif #endif
@@ -280,6 +282,7 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
DESERIALIZE(PS5Platform); DESERIALIZE(PS5Platform);
DESERIALIZE(MacPlatform); DESERIALIZE(MacPlatform);
DESERIALIZE(iOSPlatform); DESERIALIZE(iOSPlatform);
DESERIALIZE(WebPlatform);
} }
#if USE_EDITOR #if USE_EDITOR

View File

@@ -210,6 +210,14 @@ namespace FlaxEditor.Content.Settings
public JsonAsset iOSPlatform; public JsonAsset iOSPlatform;
#endif #endif
#if FLAX_EDITOR || PLATFORM_WEB
/// <summary>
/// Reference to <see cref="WebPlatformSettings"/> asset. Used to apply configuration on Web platform.
/// </summary>
[EditorOrder(2110), EditorDisplay("Platform Settings", "Web"), AssetReference(typeof(WebPlatformSettings), true), Tooltip("Reference to Web Platform Settings asset")]
public JsonAsset WebPlatform;
#endif
/// <summary> /// <summary>
/// Gets the absolute path to the game settings asset file. /// Gets the absolute path to the game settings asset file.
/// </summary> /// </summary>
@@ -345,6 +353,10 @@ namespace FlaxEditor.Content.Settings
if (type == typeof(iOSPlatformSettings)) if (type == typeof(iOSPlatformSettings))
return Load<iOSPlatformSettings>(gameSettings.iOSPlatform) as T; return Load<iOSPlatformSettings>(gameSettings.iOSPlatform) as T;
#endif #endif
#if FLAX_EDITOR || PLATFORM_WEB
if (type == typeof(WebPlatformSettings))
return Load<WebPlatformSettings>(gameSettings.WebPlatform) as T;
#endif
if (gameSettings.CustomSettings != null) if (gameSettings.CustomSettings != null)
{ {
@@ -443,6 +455,10 @@ namespace FlaxEditor.Content.Settings
if (type == typeof(iOSPlatformSettings)) if (type == typeof(iOSPlatformSettings))
return gameSettings.iOSPlatform; return gameSettings.iOSPlatform;
#endif #endif
#if FLAX_EDITOR || PLATFORM_WEB
if (type == typeof(WebPlatformSettings))
return gameSettings.WebPlatform;
#endif
if (gameSettings.CustomSettings != null) if (gameSettings.CustomSettings != null)
{ {
@@ -557,6 +573,8 @@ namespace FlaxEditor.Content.Settings
return SaveAsset(gameSettings, ref gameSettings.MacPlatform, obj); return SaveAsset(gameSettings, ref gameSettings.MacPlatform, obj);
if (type == typeof(iOSPlatformSettings)) if (type == typeof(iOSPlatformSettings))
return SaveAsset(gameSettings, ref gameSettings.iOSPlatform, obj); return SaveAsset(gameSettings, ref gameSettings.iOSPlatform, obj);
if (type == typeof(WebPlatformSettings))
return SaveAsset(gameSettings, ref gameSettings.WebPlatform, obj);
return true; return true;
} }

View File

@@ -90,6 +90,7 @@ public:
Guid PS5Platform; Guid PS5Platform;
Guid MacPlatform; Guid MacPlatform;
Guid iOSPlatform; Guid iOSPlatform;
Guid WebPlatform;
public: public:
/// <summary> /// <summary>

View File

@@ -38,3 +38,6 @@
#if PLATFORM_IOS #if PLATFORM_IOS
#include "Engine/Platform/iOS/iOSPlatformSettings.h" #include "Engine/Platform/iOS/iOSPlatformSettings.h"
#endif #endif
#if PLATFORM_WEB
#include "Engine/Platform/Web/WebPlatformSettings.h"
#endif

View File

@@ -17,7 +17,7 @@
#endif #endif
#include <iostream> #include <iostream>
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH) #define LOG_ENABLE_FILE (!PLATFORM_SWITCH && !PLATFORM_WEB)
#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_WINDOWS && PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE)) #define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_WINDOWS && PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE))
namespace namespace

View File

@@ -2,8 +2,7 @@
#pragma once #pragma once
#include "Engine/Core/Types/BaseTypes.h" #include "Math.h"
#include <math.h>
namespace Math namespace Math
{ {

View File

@@ -153,6 +153,12 @@ public:
return Math::IsOne(X) && Math::IsOne(Y) && Math::IsOne(Z) && Math::IsOne(W); return Math::IsOne(X) && Math::IsOne(Y) && Math::IsOne(Z) && Math::IsOne(W);
} }
// Calculates the length of the vector.
T Length() const
{
return Math::Sqrt(X * X + Y * Y + Z * Z + W * W);
}
/// <summary> /// <summary>
/// Returns the average arithmetic of all the components. /// Returns the average arithmetic of all the components.
/// </summary> /// </summary>

View File

@@ -58,7 +58,7 @@ public:
typedef void* Tag; typedef void* Tag;
template<typename T> template<typename T>
class alignas(sizeof(void*)) Data class alignas(uint64) Data
{ {
private: private:
byte _data[Capacity * sizeof(T)]; byte _data[Capacity * sizeof(T)];
@@ -200,13 +200,13 @@ public:
typedef void* Tag; typedef void* Tag;
template<typename T> template<typename T>
class alignas(sizeof(void*)) Data class alignas(sizeof(uint64)) Data
{ {
private: private:
typedef typename FallbackAllocation::template Data<T> FallbackData; typedef typename FallbackAllocation::template Data<T> FallbackData;
bool _useFallback = false; bool _useFallback = false;
alignas(sizeof(void*)) byte _data[Capacity * sizeof(T)]; alignas(sizeof(uint64)) byte _data[Capacity * sizeof(T)];
FallbackData _fallback; FallbackData _fallback;
public: public:

View File

@@ -1,182 +0,0 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "CommonValue.h"
#include "Engine/Scripting/ScriptingObject.h"
PRAGMA_DISABLE_DEPRECATION_WARNINGS
const CommonValue CommonValue::Zero(0.0f);
const CommonValue CommonValue::One(1.0f);
const CommonValue CommonValue::Null(static_cast<void*>(nullptr));
const CommonValue CommonValue::False(false);
const CommonValue CommonValue::True(true);
const Char* ToString(CommonType value)
{
const Char* result;
switch (value)
{
case CommonType::Bool:
result = TEXT("Bool");
break;
case CommonType::Integer:
result = TEXT("Integer");
break;
case CommonType::Float:
result = TEXT("Float");
break;
case CommonType::Vector2:
result = TEXT("Vector2");
break;
case CommonType::Vector3:
result = TEXT("Vector3");
break;
case CommonType::Vector4:
result = TEXT("Vector4");
break;
case CommonType::Color:
result = TEXT("Color");
break;
case CommonType::Guid:
result = TEXT("Guid");
break;
case CommonType::String:
result = TEXT("String");
break;
case CommonType::Box:
result = TEXT("Box");
break;
case CommonType::Rotation:
result = TEXT("Rotation");
break;
case CommonType::Transform:
result = TEXT("Transform");
break;
case CommonType::Sphere:
result = TEXT("Sphere");
break;
case CommonType::Rectangle:
result = TEXT("Rectangle");
break;
case CommonType::Pointer:
result = TEXT("Pointer");
break;
case CommonType::Matrix:
result = TEXT("Matrix");
break;
case CommonType::Blob:
result = TEXT("Blob");
break;
case CommonType::Object:
result = TEXT("Object");
break;
case CommonType::Ray:
result = TEXT("Ray");
break;
default:
result = TEXT("");
break;
}
return result;
}
bool CommonValue::NearEqual(const CommonValue& a, const CommonValue& b, float epsilon)
{
ASSERT(a.Type == b.Type);
switch (a.Type)
{
case CommonType::Bool:
return a.AsBool == b.AsBool;
case CommonType::Integer:
return Math::Abs(a.AsInteger - b.AsInteger) < epsilon;
case CommonType::Float:
return Math::Abs(a.AsFloat - b.AsFloat) < epsilon;
case CommonType::Vector2:
return Float2::NearEqual(a.AsVector2, b.AsVector2, epsilon);
case CommonType::Vector3:
return Float3::NearEqual(a.AsVector3, b.AsVector3, epsilon);
case CommonType::Vector4:
return Float4::NearEqual(a.AsVector4, b.AsVector4, epsilon);
case CommonType::Color:
return Color::NearEqual(a.AsColor, b.AsColor, epsilon);
case CommonType::Guid:
return a.AsGuid == b.AsGuid;
case CommonType::String:
return StringUtils::Compare(a.AsString, b.AsString) > 0;
case CommonType::Box:
return BoundingBox::NearEqual(a.AsBox, b.AsBox, epsilon);
case CommonType::Rotation:
return Quaternion::NearEqual(a.AsRotation, b.AsRotation, epsilon);
case CommonType::Transform:
return Transform::NearEqual(a.AsTransform, b.AsTransform, epsilon);
case CommonType::Sphere:
return BoundingSphere::NearEqual(a.AsSphere, b.AsSphere, epsilon);
case CommonType::Rectangle:
return Rectangle::NearEqual(a.AsRectangle, b.AsRectangle, epsilon);
case CommonType::Ray:
return Ray::NearEqual(a.AsRay, b.AsRay, epsilon);
case CommonType::Pointer:
case CommonType::Object:
return a.AsPointer == b.AsPointer;
case CommonType::Matrix:
return a.AsMatrix == b.AsMatrix;
case CommonType::Blob:
return a.AsBlob.Length == b.AsBlob.Length;
default: CRASH;
return false;
}
}
CommonValue CommonValue::Lerp(const CommonValue& a, const CommonValue& b, float alpha)
{
ASSERT(a.Type == b.Type);
switch (a.Type)
{
case CommonType::Bool:
return alpha < 0.5f ? a : b;
case CommonType::Integer:
return Math::Lerp(a.AsInteger, b.AsInteger, alpha);
case CommonType::Float:
return Math::Lerp(a.AsFloat, b.AsFloat, alpha);
case CommonType::Vector2:
return Float2::Lerp(a.AsVector2, b.AsVector2, alpha);
case CommonType::Vector3:
return Float3::Lerp(a.AsVector3, b.AsVector3, alpha);
case CommonType::Vector4:
return Float4::Lerp(a.AsVector4, b.AsVector4, alpha);
case CommonType::Color:
return Color::Lerp(a.AsColor, b.AsColor, alpha);
case CommonType::Box:
return BoundingBox(Vector3::Lerp(a.AsBox.Minimum, b.AsBox.Minimum, alpha), Vector3::Lerp(a.AsBox.Maximum, b.AsBox.Maximum, alpha));
case CommonType::Rotation:
return Quaternion::Lerp(a.AsRotation, b.AsRotation, alpha);
case CommonType::Transform:
return Transform::Lerp(a.AsTransform, b.AsTransform, alpha);
case CommonType::Sphere:
return BoundingSphere(Vector3::Lerp(a.AsSphere.Center, b.AsSphere.Center, alpha), Math::Lerp(a.AsSphere.Radius, b.AsSphere.Radius, alpha));
case CommonType::Rectangle:
return Rectangle(Float2::Lerp(a.AsRectangle.Location, b.AsRectangle.Location, alpha), Float2::Lerp(a.AsRectangle.Size, b.AsRectangle.Size, alpha));
case CommonType::Ray:
return Ray(Vector3::Lerp(a.AsRay.Position, b.AsRay.Position, alpha), Vector3::Normalize(Vector3::Lerp(a.AsRay.Direction, b.AsRay.Direction, alpha)));
default:
return a;
}
}
Guid CommonValue::GetObjectId() const
{
ASSERT(Type == CommonType::Object);
return AsObject ? AsObject->GetID() : Guid::Empty;
}
void CommonValue::LinkObject()
{
AsObject->Deleted.Bind<CommonValue, &CommonValue::OnObjectDeleted>(this);
}
void CommonValue::UnlinkObject()
{
AsObject->Deleted.Unbind<CommonValue, &CommonValue::OnObjectDeleted>(this);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
#include "Variant.h" #include "Variant.h"
#include "CommonValue.h"
#include "Engine/Core/Collections/HashFunctions.h" #include "Engine/Core/Collections/HashFunctions.h"
#include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Content/Asset.h" #include "Engine/Content/Asset.h"
@@ -964,78 +963,6 @@ Variant::Variant(const Span<byte>& v)
} }
} }
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#include "Engine/Content/Deprecated.h"
Variant::Variant(const CommonValue& value)
: Variant()
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
MARK_CONTENT_DEPRECATED();
switch (value.Type)
{
case CommonType::Bool:
*this = value.AsBool;
break;
case CommonType::Integer:
*this = value.AsInteger;
break;
case CommonType::Float:
*this = value.AsFloat;
break;
case CommonType::Vector2:
*this = value.AsVector2;
break;
case CommonType::Vector3:
*this = value.AsVector3;
break;
case CommonType::Vector4:
*this = value.AsVector4;
break;
case CommonType::Color:
*this = value.AsColor;
break;
case CommonType::Guid:
*this = value.AsGuid;
break;
case CommonType::String:
SetString(StringView(value.AsString));
break;
case CommonType::Box:
*this = Variant(value.AsBox);
break;
case CommonType::Rotation:
*this = value.AsRotation;
break;
case CommonType::Transform:
*this = Variant(value.AsTransform);
break;
case CommonType::Sphere:
*this = value.AsSphere;
break;
case CommonType::Rectangle:
*this = value.AsRectangle;
break;
case CommonType::Pointer:
*this = value.AsPointer;
break;
case CommonType::Matrix:
*this = Variant(value.AsMatrix);
break;
case CommonType::Blob:
SetBlob(value.AsBlob.Data, value.AsBlob.Length);
break;
case CommonType::Object:
SetObject(value.AsObject);
break;
case CommonType::Ray:
*this = Variant(value.AsRay);
break;
default:
CRASH;
}
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
Variant::~Variant() Variant::~Variant()
{ {
switch (Type.Type) switch (Type.Type)

View File

@@ -7,7 +7,6 @@
class Asset; class Asset;
struct Transform; struct Transform;
struct CommonValue;
template<typename T> template<typename T>
class AssetReference; class AssetReference;
struct ScriptingTypeHandle; struct ScriptingTypeHandle;
@@ -264,7 +263,6 @@ public:
explicit Variant(Dictionary<Variant, Variant, HeapAllocation>&& v); explicit Variant(Dictionary<Variant, Variant, HeapAllocation>&& v);
explicit Variant(const Dictionary<Variant, Variant, HeapAllocation>& v); explicit Variant(const Dictionary<Variant, Variant, HeapAllocation>& v);
explicit Variant(const Span<byte>& v); explicit Variant(const Span<byte>& v);
explicit Variant(const CommonValue& v);
template<typename T> template<typename T>
Variant(const class AssetReference<T>& v) Variant(const class AssetReference<T>& v)

View File

@@ -26,6 +26,8 @@
#include "Mac/MacGame.h" #include "Mac/MacGame.h"
#elif PLATFORM_IOS #elif PLATFORM_IOS
#include "iOS/iOSGame.h" #include "iOS/iOSGame.h"
#elif PLATFORM_WEB
#include "Web/WebGame.h"
#else #else
#error Missing Game implementation! #error Missing Game implementation!
#endif #endif

View File

@@ -2,60 +2,12 @@
#include "GameplayGlobals.h" #include "GameplayGlobals.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/Types/CommonValue.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/BinaryAssetUpgrader.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#if USE_EDITOR REGISTER_BINARY_ASSET(GameplayGlobals, "FlaxEngine.GameplayGlobals", true);
class GameplayGlobalsUpgrader : public BinaryAssetUpgrader
{
public:
GameplayGlobalsUpgrader()
{
const Upgrader upgraders[] =
{
{ 1, 2, &Upgrade_1_To_2 }, // [Deprecated on 31.07.2020, expires on 31.07.2022]
};
setup(upgraders, ARRAY_COUNT(upgraders));
}
private:
static bool Upgrade_1_To_2(AssetMigrationContext& context)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
PRAGMA_DISABLE_DEPRECATION_WARNINGS
ASSERT(context.Input.SerializedVersion == 1 && context.Output.SerializedVersion == 2);
if (context.AllocateChunk(0))
return true;
auto& data = context.Input.Header.Chunks[0]->Data;
MemoryReadStream stream(data.Get(), data.Length());
MemoryWriteStream output;
int32 count;
stream.ReadInt32(&count);
output.WriteInt32(count);
String name;
for (int32 i = 0; i < count; i++)
{
stream.Read(name, 71);
CommonValue commonValue;
stream.ReadCommonValue(&commonValue);
Variant variant(commonValue);
output.WriteVariant(variant);
}
context.Output.Header.Chunks[0]->Data.Copy(output.GetHandle(), output.GetPosition());
PRAGMA_ENABLE_DEPRECATION_WARNINGS
return false;
}
};
#endif
REGISTER_BINARY_ASSET_WITH_UPGRADER(GameplayGlobals, "FlaxEngine.GameplayGlobals", GameplayGlobalsUpgrader, true);
GameplayGlobals::GameplayGlobals(const SpawnParams& params, const AssetInfo* info) GameplayGlobals::GameplayGlobals(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info) : BinaryAsset(params, info)

View File

@@ -0,0 +1,19 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_WEB
#include "../Base/GameBase.h"
/// <summary>
/// The game class implementation for Web platform.
/// </summary>
/// <seealso cref="Game" />
class WebGame : public GameBase
{
};
typedef WebGame Game;
#endif

View File

@@ -45,7 +45,9 @@
#define GPU_USE_WINDOW_SRV 1 #define GPU_USE_WINDOW_SRV 1
// True if allow graphics profile events and markers // True if allow graphics profile events and markers
#ifndef GPU_ALLOW_PROFILE_EVENTS
#define GPU_ALLOW_PROFILE_EVENTS (!BUILD_RELEASE) #define GPU_ALLOW_PROFILE_EVENTS (!BUILD_RELEASE)
#endif
// True if allow hardware tessellation shaders (Hull and Domain shaders) // True if allow hardware tessellation shaders (Hull and Domain shaders)
#ifndef GPU_ALLOW_TESSELLATION_SHADERS #ifndef GPU_ALLOW_TESSELLATION_SHADERS
@@ -58,7 +60,9 @@
#endif #endif
// Enable/disable creating GPU resources on separate threads (otherwise only the main thread can be used) // Enable/disable creating GPU resources on separate threads (otherwise only the main thread can be used)
#ifndef GPU_ENABLE_ASYNC_RESOURCES_CREATION
#define GPU_ENABLE_ASYNC_RESOURCES_CREATION 1 #define GPU_ENABLE_ASYNC_RESOURCES_CREATION 1
#endif
// Enable/disable force shaders recompilation // Enable/disable force shaders recompilation
#define GPU_FORCE_RECOMPILE_SHADERS 0 #define GPU_FORCE_RECOMPILE_SHADERS 0

View File

@@ -96,6 +96,10 @@ public class Graphics : EngineModule
else else
Log.WarningOnce(string.Format("Building for {0} without Vulkan rendering backend (Vulkan SDK is missing)", options.Platform.Target), ref _logMissingVulkanSDK); Log.WarningOnce(string.Format("Building for {0} without Vulkan rendering backend (Vulkan SDK is missing)", options.Platform.Target), ref _logMissingVulkanSDK);
break; break;
case TargetPlatform.Web:
options.PrivateDependencies.Add("GraphicsDeviceNull");
// TODO: add WebGPU
break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);
} }

View File

@@ -74,7 +74,7 @@ struct TIsPODType<SkeletonBone>
class FLAXENGINE_API SkeletonData class FLAXENGINE_API SkeletonData
{ {
private: private:
mutable volatile int64 _dirty = 1; mutable volatile intptr _dirty = 1;
mutable Array<Matrix> _cachedPose; mutable Array<Matrix> _cachedPose;
public: public:

View File

@@ -127,7 +127,7 @@ GPUTexture* RenderBuffers::RequestHiZ(GPUContext* context, bool fullRes, int32 m
// Allocate or resize buffer (with full mip-chain) // Allocate or resize buffer (with full mip-chain)
// TODO: migrate to inverse depth and try using r16 again as default (should have no artifacts anymore) // TODO: migrate to inverse depth and try using r16 again as default (should have no artifacts anymore)
auto format = PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? PixelFormat::R16_UInt : PixelFormat::R32_Float; auto format = PLATFORM_WEB || PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? PixelFormat::R16_UInt : PixelFormat::R32_Float;
auto width = fullRes ? _width : Math::Max(_width >> 1, 1); auto width = fullRes ? _width : Math::Max(_width >> 1, 1);
auto height = fullRes ? _height : Math::Max(_height >> 1, 1); auto height = fullRes ? _height : Math::Max(_height >> 1, 1);
auto desc = GPUTextureDescription::New2D(width, height, mipLevels, format, GPUTextureFlags::ShaderResource); auto desc = GPUTextureDescription::New2D(width, height, mipLevels, format, GPUTextureFlags::ShaderResource);

View File

@@ -428,7 +428,9 @@ void LevelService::LateFixedUpdate()
void LevelService::Dispose() void LevelService::Dispose()
{ {
// End scene actions
ScopeLock lock(_sceneActionsLocker); ScopeLock lock(_sceneActionsLocker);
_sceneActions.ClearDelete();
// Unload scenes // Unload scenes
unloadScenes(); unloadScenes();

View File

@@ -88,6 +88,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
// Draw all visual components // Draw all visual components
_drawListIndex = -1; _drawListIndex = -1;
#if PLATFORM_THREADS_LIMIT > 1
if (_drawListSize >= 64 && category == SceneDrawAsync && renderContextBatch.EnableAsync) if (_drawListSize >= 64 && category == SceneDrawAsync && renderContextBatch.EnableAsync)
{ {
// Run in async via Job System // Run in async via Job System
@@ -97,6 +98,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
renderContextBatch.WaitLabels.Add(waitLabel); renderContextBatch.WaitLabels.Add(waitLabel);
} }
else else
#endif
{ {
// Scene is small so draw on a main-thread // Scene is small so draw on a main-thread
DrawActorsJob(0); DrawActorsJob(0);

View File

@@ -262,6 +262,7 @@ void LocalizationService::OnLocalizationChanged()
localeName[currentCulture.Length() + 5] = '8'; localeName[currentCulture.Length() + 5] = '8';
localeName[currentCulture.Length() + 6] = 0; localeName[currentCulture.Length() + 6] = 0;
} }
#if HAS_EXCEPTIONS
try try
{ {
std::locale::global(std::locale(localeName)); std::locale::global(std::locale(localeName));
@@ -272,6 +273,9 @@ void LocalizationService::OnLocalizationChanged()
catch (...) catch (...)
{ {
} }
#else
std::locale::global(std::locale(localeName));
#endif
} }
#endif #endif

View File

@@ -74,6 +74,9 @@ public class Main : EngineModule
case TargetPlatform.iOS: case TargetPlatform.iOS:
options.SourcePaths.Add(Path.Combine(FolderPath, "Default")); options.SourcePaths.Add(Path.Combine(FolderPath, "Default"));
break; break;
case TargetPlatform.Web:
options.SourcePaths.Add(Path.Combine(FolderPath, "Web"));
break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);
} }
} }

View File

@@ -0,0 +1,12 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#if PLATFORM_WEB
#include "Engine/Engine/Engine.h"
int main()
{
return Engine::Main(TEXT(""));
}
#endif

View File

@@ -6,23 +6,6 @@
class ScriptingObject; class ScriptingObject;
/// <summary>
/// Network driver implementations enum.
/// [Deprecated in v1.3]
/// </summary>
API_ENUM(Namespace="FlaxEngine.Networking") enum class DEPRECATED() NetworkDriverType
{
/// <summary>
/// Invalid network driver implementation.
/// </summary>
Undefined = 0,
/// <summary>
/// ENet library based network driver implementation.
/// </summary>
ENet
};
/// <summary> /// <summary>
/// Low-level network configuration structure. Provides settings for the network driver and all internal components. /// Low-level network configuration structure. Provides settings for the network driver and all internal components.
/// </summary> /// </summary>
@@ -30,14 +13,6 @@ API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkConfi
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkConfig); DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkConfig);
/// <summary>
/// The network driver that will be used to create the peer.
/// To allow two peers to connect, they must use the same host.
/// [Deprecated in v1.3]
/// </summary>
API_FIELD()
DEPRECATED("Use NetworkDriver field instead") NetworkDriverType NetworkDriverType;
/// <summary> /// <summary>
/// The network driver instance (implements INetworkDriver) that will be used to create and manage the peer, send and receive messages. /// The network driver instance (implements INetworkDriver) that will be used to create and manage the peer, send and receive messages.
/// </summary> /// </summary>
@@ -81,15 +56,4 @@ API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkConfi
/// </remarks> /// </remarks>
API_FIELD() API_FIELD()
uint16 MessagePoolSize = 2048; uint16 MessagePoolSize = 2048;
// Ignore deprecation warnings in defaults
PRAGMA_DISABLE_DEPRECATION_WARNINGS
NetworkConfig()
{
NetworkDriverType = NetworkDriverType::ENet;
}
NetworkConfig(const NetworkConfig& other) = default;
NetworkConfig(NetworkConfig&& other) = default;
NetworkConfig& operator=(const NetworkConfig& other) = default;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}; };

View File

@@ -22,10 +22,6 @@ bool NetworkPeer::Initialize(const NetworkConfig& config)
return true; return true;
Config = config; Config = config;
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (Config.NetworkDriver == nullptr && Config.NetworkDriverType == NetworkDriverType::ENet)
Config.NetworkDriver = New<ENetDriver>();
PRAGMA_ENABLE_DEPRECATION_WARNINGS
if (Config.NetworkDriver == nullptr) if (Config.NetworkDriver == nullptr)
{ {

View File

@@ -3,10 +3,10 @@
#if COMPILE_WITH_PARTICLE_GPU_GRAPH #if COMPILE_WITH_PARTICLE_GPU_GRAPH
#include "ParticleEmitterGraph.GPU.h" #include "ParticleEmitterGraph.GPU.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Serialization/FileReadStream.h" #include "Engine/Serialization/FileReadStream.h"
#include "Engine/Visject/ShaderGraphUtilities.h" #include "Engine/Visject/ShaderGraphUtilities.h"
#include "Engine/Engine/Globals.h" #include "Engine/Engine/Globals.h"
#include "Engine/Core/Types/CommonValue.h"
/// <summary> /// <summary>
/// GPU particles shader source code template has special marks for generated code. /// GPU particles shader source code template has special marks for generated code.

View File

@@ -2,7 +2,6 @@
#include "ParticleEffect.h" #include "ParticleEffect.h"
#include "Particles.h" #include "Particles.h"
#include "Engine/Core/Types/CommonValue.h"
#include "Engine/Content/Deprecated.h" #include "Engine/Content/Deprecated.h"
#include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonTools.h"
#include "Engine/Serialization/Serialization.h" #include "Engine/Serialization/Serialization.h"
@@ -728,86 +727,39 @@ void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier*
const auto overridesMember = stream.FindMember("Overrides"); const auto overridesMember = stream.FindMember("Overrides");
if (overridesMember != stream.MemberEnd()) if (overridesMember != stream.MemberEnd())
{ {
// [Deprecated on 25.11.2018, expires on 25.11.2022] const auto& overrides = overridesMember->value;
if (modifier->EngineBuild < 6197) CHECK(overrides.IsArray());
_parametersOverrides.EnsureCapacity(_parametersOverrides.Count() + overrides.Size());
for (rapidjson::SizeType i = 0; i < overrides.Size(); i++)
{ {
PRAGMA_DISABLE_DEPRECATION_WARNINGS auto& o = (DeserializeStream&)overrides[i];
MARK_CONTENT_DEPRECATED(); const String trackName = JsonTools::GetString(o, "Track");
const auto& overrides = overridesMember->value; const Guid id = JsonTools::GetGuid(o, "Id");
ASSERT(overrides.IsArray()); ParameterOverride* e = nullptr;
_parametersOverrides.EnsureCapacity(_parametersOverrides.Count() + overrides.Size()); for (auto& q : _parametersOverrides)
for (rapidjson::SizeType i = 0; i < overrides.Size(); i++)
{ {
const auto& o = (DeserializeStream&)overrides[i]; if (q.Id == id && q.Track == trackName)
const String trackName = JsonTools::GetString(o, "Track");
const Guid id = JsonTools::GetGuid(o, "Id");
ParameterOverride* e = nullptr;
for (auto& q : _parametersOverrides)
{ {
if (q.Id == id && q.Track == trackName) e = &q;
{ break;
e = &q;
break;
}
}
if (e)
{
// Update overriden parameter value
CommonValue value;
auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
if (mValue != o.MemberEnd())
e->Value = Variant(JsonTools::GetCommonValue(mValue->value));
}
else
{
// Add parameter override
auto& p = _parametersOverrides.AddOne();
p.Track = trackName;
p.Id = id;
CommonValue value;
auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
if (mValue != o.MemberEnd())
p.Value = Variant(JsonTools::GetCommonValue(mValue->value));
} }
} }
PRAGMA_ENABLE_DEPRECATION_WARNINGS if (e)
}
else
{
const auto& overrides = overridesMember->value;
ASSERT(overrides.IsArray());
_parametersOverrides.EnsureCapacity(_parametersOverrides.Count() + overrides.Size());
for (rapidjson::SizeType i = 0; i < overrides.Size(); i++)
{ {
auto& o = (DeserializeStream&)overrides[i]; // Update overriden parameter value
const String trackName = JsonTools::GetString(o, "Track"); const auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
const Guid id = JsonTools::GetGuid(o, "Id"); if (mValue != stream.MemberEnd())
ParameterOverride* e = nullptr; Serialization::Deserialize(mValue->value, e->Value, modifier);
for (auto& q : _parametersOverrides) }
{ else
if (q.Id == id && q.Track == trackName) {
{ // Add parameter override
e = &q; auto& p = _parametersOverrides.AddOne();
break; p.Track = trackName;
} p.Id = id;
} const auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
if (e) if (mValue != stream.MemberEnd())
{ Serialization::Deserialize(mValue->value, p.Value, modifier);
// Update overriden parameter value
const auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
if (mValue != stream.MemberEnd())
Serialization::Deserialize(mValue->value, e->Value, modifier);
}
else
{
// Add parameter override
auto& p = _parametersOverrides.AddOne();
p.Track = trackName;
p.Id = id;
const auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
if (mValue != stream.MemberEnd())
Serialization::Deserialize(mValue->value, p.Value, modifier);
}
} }
} }
} }

View File

@@ -4,6 +4,7 @@
#include "ParticleSystem.h" #include "ParticleSystem.h"
#include "ParticleEffect.h" #include "ParticleEffect.h"
#include "Particles.h" #include "Particles.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/ShaderAssetUpgrader.h" #include "Engine/Content/Upgraders/ShaderAssetUpgrader.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"

View File

@@ -2,7 +2,6 @@
#include "ParticleSystem.h" #include "ParticleSystem.h"
#include "ParticleEffect.h" #include "ParticleEffect.h"
#include "Engine/Core/Types/CommonValue.h"
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
#include "Engine/Content/Deprecated.h" #include "Engine/Content/Deprecated.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Factories/BinaryAssetFactory.h"
@@ -225,158 +224,6 @@ Asset::LoadResult ParticleSystem::load()
#endif #endif
switch (version) switch (version)
{ {
PRAGMA_DISABLE_DEPRECATION_WARNINGS
case 1:
{
// [Deprecated on 23.07.2019, expires on 27.04.2021]
MARK_CONTENT_DEPRECATED();
// Load properties
stream.ReadFloat(&FramesPerSecond);
stream.ReadInt32(&DurationFrames);
// Load emitters
int32 emittersCount;
stream.ReadInt32(&emittersCount);
Emitters.Resize(emittersCount, false);
// Load tracks
Guid id;
int32 tracksCount;
stream.ReadInt32(&tracksCount);
Tracks.Resize(tracksCount, false);
for (int32 i = 0; i < tracksCount; i++)
{
auto& track = Tracks[i];
track.Type = (Track::Types)stream.ReadByte();
track.Flag = (Track::Flags)stream.ReadByte();
stream.ReadInt32(&track.ParentIndex);
stream.ReadInt32(&track.ChildrenCount);
stream.ReadString(&track.Name, -13);
track.Disabled = (int32)track.Flag & (int32)Track::Flags::Mute || (track.ParentIndex != -1 && Tracks[track.ParentIndex].Disabled);
switch (track.Type)
{
case Track::Types::Emitter:
stream.Read(id);
stream.ReadInt32(&track.AsEmitter.Index);
stream.ReadInt32(&track.AsEmitter.StartFrame);
stream.ReadInt32(&track.AsEmitter.DurationFrames);
Emitters[track.AsEmitter.Index] = id;
break;
case Track::Types::Folder:
stream.Read(track.Color);
break;
default:
return LoadResult::InvalidData;
}
}
// Wait for all tracks to be loaded - particle system cannot be used if any of the emitters is not loaded yet
// Note: this loop might trigger loading referenced assets on this thread
for (int32 i = 0; i < Emitters.Count(); i++)
{
if (Emitters[i])
Emitters[i]->WaitForLoaded();
}
// Load parameters overrides
int32 overridesCount = 0;
if (stream.CanRead())
stream.ReadInt32(&overridesCount);
if (overridesCount != 0)
{
EmitterParameterOverrideKey key;
CommonValue value;
for (int32 i = 0; i < overridesCount; i++)
{
stream.ReadInt32(&key.First);
stream.Read(key.Second);
stream.ReadCommonValue(&value);
SKIP_UNUSED_PARAM_OVERRIDE();
EmittersParametersOverrides.Add(key, Variant(value));
}
}
break;
}
case 2:
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
MARK_CONTENT_DEPRECATED();
// Load properties
stream.ReadFloat(&FramesPerSecond);
stream.ReadInt32(&DurationFrames);
// Load emitters
int32 emittersCount;
stream.ReadInt32(&emittersCount);
Emitters.Resize(emittersCount, false);
// Load tracks
Guid id;
int32 tracksCount;
stream.ReadInt32(&tracksCount);
Tracks.Resize(tracksCount, false);
for (int32 i = 0; i < tracksCount; i++)
{
auto& track = Tracks[i];
track.Type = (Track::Types)stream.ReadByte();
track.Flag = (Track::Flags)stream.ReadByte();
stream.ReadInt32(&track.ParentIndex);
stream.ReadInt32(&track.ChildrenCount);
stream.ReadString(&track.Name, -13);
track.Disabled = (int32)track.Flag & (int32)Track::Flags::Mute || (track.ParentIndex != -1 && Tracks[track.ParentIndex].Disabled);
stream.Read(track.Color);
switch (track.Type)
{
case Track::Types::Emitter:
stream.Read(id);
stream.ReadInt32(&track.AsEmitter.Index);
stream.ReadInt32(&track.AsEmitter.StartFrame);
stream.ReadInt32(&track.AsEmitter.DurationFrames);
Emitters[track.AsEmitter.Index] = id;
break;
case Track::Types::Folder:
break;
default:
return LoadResult::InvalidData;
}
}
// Wait for all tracks to be loaded - particle system cannot be used if any of the emitters is not loaded yet
// Note: this loop might trigger loading referenced assets on this thread
for (int32 i = 0; i < Emitters.Count(); i++)
{
if (Emitters[i])
Emitters[i]->WaitForLoaded();
}
// Load parameters overrides
int32 overridesCount = 0;
if (stream.CanRead())
stream.ReadInt32(&overridesCount);
if (overridesCount != 0)
{
EmitterParameterOverrideKey key;
CommonValue value;
for (int32 i = 0; i < overridesCount; i++)
{
stream.ReadInt32(&key.First);
stream.Read(key.Second);
stream.ReadCommonValue(&value);
SKIP_UNUSED_PARAM_OVERRIDE();
EmittersParametersOverrides[key] = Variant(value);
}
}
break;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
case 3: // [Deprecated on 03.09.2021 expires on 03.09.2023] case 3: // [Deprecated on 03.09.2021 expires on 03.09.2023]
MARK_CONTENT_DEPRECATED(); MARK_CONTENT_DEPRECATED();
case 4: case 4:

View File

@@ -41,7 +41,7 @@ PACK_STRUCT(struct SpriteParticleVertex
class SpriteParticleRenderer class SpriteParticleRenderer
{ {
public: public:
volatile int64 Ready = 0; volatile intptr Ready = 0;
GPUBuffer* VB = nullptr; GPUBuffer* VB = nullptr;
GPUBuffer* IB = nullptr; GPUBuffer* IB = nullptr;
const static int32 VertexCount = 4; const static int32 VertexCount = 4;

View File

@@ -6,7 +6,6 @@
#include "Particles.h" #include "Particles.h"
#include "Engine/Graphics/GPUBuffer.h" #include "Engine/Graphics/GPUBuffer.h"
#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUDevice.h"
#include "Engine/Core/Types/CommonValue.h"
ParticleEmitterInstance::ParticleEmitterInstance() ParticleEmitterInstance::ParticleEmitterInstance()
{ {

View File

@@ -539,11 +539,35 @@ protected:
hitInfo = hit.shape ? static_cast<PhysicsColliderActor*>(hit.shape->userData) : nullptr; \ hitInfo = hit.shape ? static_cast<PhysicsColliderActor*>(hit.shape->userData) : nullptr; \
} }
#if PLATFORM_THREADS_LIMIT <= 1
class DummyCpuDispatcher : public PxCpuDispatcher
{
public:
void submitTask(PxBaseTask& task) override
{
// Run directly
PROFILE_CPU_NAMED("Physics");
task.run();
task.release();
}
uint32_t getWorkerCount() const override
{
return 1;
}
};
#endif
namespace namespace
{ {
PxFoundation* Foundation = nullptr; PxFoundation* Foundation = nullptr;
PxPhysics* PhysX = nullptr; PxPhysics* PhysX = nullptr;
#if PLATFORM_THREADS_LIMIT > 1
PxDefaultCpuDispatcher* CpuDispatcher = nullptr; PxDefaultCpuDispatcher* CpuDispatcher = nullptr;
#else
DummyCpuDispatcher* CpuDispatcher = nullptr;
#endif
#if WITH_PVD #if WITH_PVD
PxPvd* PVD = nullptr; PxPvd* PVD = nullptr;
#endif #endif
@@ -1736,7 +1760,11 @@ void PhysicsBackend::Shutdown()
#if WITH_PVD #if WITH_PVD
RELEASE_PHYSX(PVD); RELEASE_PHYSX(PVD);
#endif #endif
#if PLATFORM_THREADS_LIMIT > 1
RELEASE_PHYSX(CpuDispatcher); RELEASE_PHYSX(CpuDispatcher);
#else
SAFE_DELETE(CpuDispatcher);
#endif
RELEASE_PHYSX(Foundation); RELEASE_PHYSX(Foundation);
SceneOrigins.Clear(); SceneOrigins.Clear();
} }
@@ -1796,9 +1824,14 @@ void* PhysicsBackend::CreateScene(const PhysicsSettings& settings)
{ {
if (CpuDispatcher == nullptr) if (CpuDispatcher == nullptr)
{ {
#if PLATFORM_THREADS_LIMIT > 1
uint32 threads = Math::Clamp<uint32>(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 8); uint32 threads = Math::Clamp<uint32>(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 8);
CpuDispatcher = PxDefaultCpuDispatcherCreate(threads); CpuDispatcher = PxDefaultCpuDispatcherCreate(threads);
CHECK_INIT(CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!"); CHECK_INIT(CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!");
#else
CpuDispatcher = New<DummyCpuDispatcher>();
#endif
CHECK_INIT(CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!");
} }
sceneDesc.cpuDispatcher = CpuDispatcher; sceneDesc.cpuDispatcher = CpuDispatcher;
} }

View File

@@ -39,6 +39,12 @@ public class Physics : EngineModule
{ {
base.Setup(options); base.Setup(options);
if (options.Platform.Target == TargetPlatform.Web) // TODO: build PhysX for Web
{
options.PrivateDefinitions.Add("COMPILE_WITH_EMPTY_PHYSICS");
return;
}
SetupPhysicsBackend(this, options); SetupPhysicsBackend(this, options);
if (WithCooking) if (WithCooking)

View File

@@ -99,11 +99,6 @@ public:
static void Yield(); static void Yield();
static double GetTimeSeconds(); static double GetTimeSeconds();
static uint64 GetTimeCycles(); static uint64 GetTimeCycles();
FORCE_INLINE static uint64 GetClockFrequency()
{
// Dummy value
return 1000000;
}
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond); static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond); static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
static bool Init(); static bool Init();

View File

@@ -244,11 +244,11 @@ bool FileSystemBase::CopyDirectory(const String& dst, const String& src, bool wi
return !FileSystem::DirectoryExists(*src) || FileSystemBase::DirectoryCopyHelper(dst, src, withSubDirectories); return !FileSystem::DirectoryExists(*src) || FileSystemBase::DirectoryCopyHelper(dst, src, withSubDirectories);
} }
uint64 FileSystemBase::GetDirectorySize(const StringView& path) uint64 FileSystemBase::GetDirectorySize(const StringView& path, const Char* searchPattern, DirectorySearchOption option)
{ {
uint64 result = 0; uint64 result = 0;
Array<String> files; Array<String> files;
FileSystem::DirectoryGetFiles(files, path); FileSystem::DirectoryGetFiles(files, path, searchPattern, option);
for (const String& file : files) for (const String& file : files)
result += FileSystem::GetFileSize(file); result += FileSystem::GetFileSize(file);
return result; return result;

View File

@@ -98,8 +98,10 @@ class FLAXENGINE_API FileSystemBase
/// Gets the size of the directory (in bytes) defined by size of all files contained by it. /// Gets the size of the directory (in bytes) defined by size of all files contained by it.
/// </summary> /// </summary>
/// <param name="path">Directory path.</param> /// <param name="path">Directory path.</param>
/// <param name="searchPattern">Custom search pattern to use during that operation Use asterisk character (*) for name-based filtering (eg. `*.txt` to find all files with `.txt` extension).</param>
/// <param name="option">Additional search options that define rules.</param>
/// <returns>Amount of bytes in directory, or 0 if failed.</returns> /// <returns>Amount of bytes in directory, or 0 if failed.</returns>
static uint64 GetDirectorySize(const StringView& path); static uint64 GetDirectorySize(const StringView& path, const Char* searchPattern = TEXT("*"), DirectorySearchOption option = DirectorySearchOption::AllDirectories);
public: public:
/// <summary> /// <summary>

View File

@@ -44,7 +44,7 @@ static_assert(sizeof(double) == 8, "Invalid double type size.");
// Check configuration // Check configuration
static_assert((PLATFORM_THREADS_LIMIT & (PLATFORM_THREADS_LIMIT - 1)) == 0, "Threads limit must be power of two."); static_assert((PLATFORM_THREADS_LIMIT & (PLATFORM_THREADS_LIMIT - 1)) == 0, "Threads limit must be power of two.");
static_assert(PLATFORM_THREADS_LIMIT % 4 == 0, "Threads limit must be multiple of 4."); static_assert(PLATFORM_THREADS_LIMIT % 4 == 0 || PLATFORM_THREADS_LIMIT == 1, "Threads limit must be multiple of 4.");
const Char* PlatformBase::ApplicationClassName = TEXT("FlaxWindow"); const Char* PlatformBase::ApplicationClassName = TEXT("FlaxWindow");
float PlatformBase::CustomDpiScale = 1.0f; float PlatformBase::CustomDpiScale = 1.0f;
@@ -301,6 +301,15 @@ bool PlatformBase::Is64BitApp()
#endif #endif
} }
bool PlatformBase::Is64BitPlatform()
{
#if PLATFORM_64BITS
return true;
#else
return false;
#endif
}
int32 PlatformBase::GetCacheLineSize() int32 PlatformBase::GetCacheLineSize()
{ {
return (int32)Platform::GetCPUInfo().CacheLineSize; return (int32)Platform::GetCPUInfo().CacheLineSize;
@@ -820,6 +829,8 @@ const Char* ToString(PlatformType type)
return TEXT("Mac"); return TEXT("Mac");
case PlatformType::iOS: case PlatformType::iOS:
return TEXT("iOS"); return TEXT("iOS");
case PlatformType::Web:
return TEXT("Web");
default: default:
return TEXT(""); return TEXT("");
} }

View File

@@ -378,7 +378,7 @@ public:
/// Returns true if running on 64-bit computer /// Returns true if running on 64-bit computer
/// </summary> /// </summary>
/// <returns>True if running on 64-bit computer, otherwise false.</returns> /// <returns>True if running on 64-bit computer, otherwise false.</returns>
API_PROPERTY() static bool Is64BitPlatform() = delete; API_PROPERTY() static bool Is64BitPlatform();
/// <summary> /// <summary>
/// Gets the name of the operating system. /// Gets the name of the operating system.
@@ -470,7 +470,11 @@ public:
/// Gets the system clock frequency. /// Gets the system clock frequency.
/// </summary> /// </summary>
/// <returns>The clock frequency.</returns> /// <returns>The clock frequency.</returns>
API_PROPERTY() static uint64 GetClockFrequency() = delete; API_PROPERTY() static uint64 GetClockFrequency()
{
// Dummy value
return 1000000;
}
/// <summary> /// <summary>
/// Gets current system time based on current computer settings. /// Gets current system time based on current computer settings.

View File

@@ -357,6 +357,8 @@ StringView StringUtils::GetPathWithoutExtension(const StringView& path)
void StringUtils::PathRemoveRelativeParts(String& path) void StringUtils::PathRemoveRelativeParts(String& path)
{ {
FileSystem::NormalizePath(path); FileSystem::NormalizePath(path);
if (path.Length() == 1 && path[0] == TEXT('/'))
return;
Array<String> components; Array<String> components;
path.Split(TEXT('/'), components); path.Split(TEXT('/'), components);

View File

@@ -45,10 +45,15 @@ void ThreadBase::Kill(bool waitForJoin)
if (!_isRunning) if (!_isRunning)
{ {
ClearHandleInternal(); ClearHandleInternal();
if (_callAfterWork)
{
_callAfterWork = false;
_runnable->AfterWork(true);
}
return; return;
} }
ASSERT(GetID()); ASSERT(GetID());
const auto thread = static_cast<Thread*>(this); Thread* thread = (Thread*)this;
// Stop runnable object // Stop runnable object
if (_callAfterWork && _runnable) if (_callAfterWork && _runnable)

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32ConditionVariable.h" #include "Win32/Win32ConditionVariable.h"
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS #elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS || PLATFORM_WEB
#include "Unix/UnixConditionVariable.h" #include "Unix/UnixConditionVariable.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchConditionVariable.h" #include "Platforms/Switch/Engine/Platform/SwitchConditionVariable.h"

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32CriticalSection.h" #include "Win32/Win32CriticalSection.h"
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS #elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS || PLATFORM_WEB
#include "Unix/UnixCriticalSection.h" #include "Unix/UnixCriticalSection.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchCriticalSection.h" #include "Platforms/Switch/Engine/Platform/SwitchCriticalSection.h"

View File

@@ -67,6 +67,11 @@ API_ENUM() enum class PlatformType
/// </summary> /// </summary>
API_ENUM(Attributes="EditorDisplay(null, \"iOS\")") API_ENUM(Attributes="EditorDisplay(null, \"iOS\")")
iOS = 11, iOS = 11,
/// <summary>
/// Running on Web.
/// </summary>
Web = 12,
}; };
/// <summary> /// <summary>
@@ -143,6 +148,9 @@ API_ENUM() enum class ArchitectureType
#if !defined(PLATFORM_SDL) #if !defined(PLATFORM_SDL)
#define PLATFORM_SDL 0 #define PLATFORM_SDL 0
#endif #endif
#if !defined(PLATFORM_WEB)
#define PLATFORM_WEB 0
#endif
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
#include "Windows/WindowsDefines.h" #include "Windows/WindowsDefines.h"
@@ -166,6 +174,8 @@ API_ENUM() enum class ArchitectureType
#include "Mac/MacDefines.h" #include "Mac/MacDefines.h"
#elif PLATFORM_IOS #elif PLATFORM_IOS
#include "iOS/iOSDefines.h" #include "iOS/iOSDefines.h"
#elif PLATFORM_WEB
#include "Web/WebDefines.h"
#else #else
#error Missing Defines implementation! #error Missing Defines implementation!
#endif #endif

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32File.h" #include "Win32/Win32File.h"
#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC #elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_WEB
#include "Unix/UnixFile.h" #include "Unix/UnixFile.h"
#elif PLATFORM_IOS #elif PLATFORM_IOS
#include "iOS/iOSFile.h" #include "iOS/iOSFile.h"

View File

@@ -24,6 +24,8 @@
#include "Mac/MacFileSystem.h" #include "Mac/MacFileSystem.h"
#elif PLATFORM_IOS #elif PLATFORM_IOS
#include "iOS/iOSFileSystem.h" #include "iOS/iOSFileSystem.h"
#elif PLATFORM_WEB
#include "Web/WebFileSystem.h"
#else #else
#error Missing File System implementation! #error Missing File System implementation!
#endif #endif

View File

@@ -114,11 +114,6 @@ public:
static void Yield(); static void Yield();
static double GetTimeSeconds(); static double GetTimeSeconds();
static uint64 GetTimeCycles(); static uint64 GetTimeCycles();
FORCE_INLINE static uint64 GetClockFrequency()
{
// Dummy value
return 1000000;
}
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond); static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond); static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
#if !BUILD_RELEASE #if !BUILD_RELEASE

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32Network.h" #include "Win32/Win32Network.h"
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_MAC || PLATFORM_IOS #elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_MAC || PLATFORM_IOS || PLATFORM_WEB
#include "Unix/UnixNetwork.h" #include "Unix/UnixNetwork.h"
#elif PLATFORM_PS4 #elif PLATFORM_PS4
#include "Platforms/PS4/Engine/Platform/PS4Network.h" #include "Platforms/PS4/Engine/Platform/PS4Network.h"

View File

@@ -87,20 +87,17 @@ public class Platform : EngineModule
options.SourcePaths.Add(Path.Combine(FolderPath, "Apple")); options.SourcePaths.Add(Path.Combine(FolderPath, "Apple"));
options.SourcePaths.Add(Path.Combine(FolderPath, "iOS")); options.SourcePaths.Add(Path.Combine(FolderPath, "iOS"));
break; break;
case TargetPlatform.Web:
options.SourcePaths.Add(Path.Combine(FolderPath, "Unix"));
options.SourcePaths.Add(Path.Combine(FolderPath, "Web"));
break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);
} }
if (EngineConfiguration.WithSDL(options)) if (EngineConfiguration.WithSDL(options))
{ {
switch (options.Platform.Target) options.PublicDependencies.Add("SDL");
{ options.SourcePaths.Add(Path.Combine(FolderPath, "SDL"));
case TargetPlatform.Windows:
case TargetPlatform.Linux:
case TargetPlatform.Mac:
options.PublicDependencies.Add("SDL");
options.SourcePaths.Add(Path.Combine(FolderPath, "SDL"));
break;
}
if (options.Platform.Target == TargetPlatform.Linux) if (options.Platform.Target == TargetPlatform.Linux)
options.PublicDependencies.Add("Wayland"); options.PublicDependencies.Add("Wayland");
} }
@@ -115,6 +112,7 @@ public class Platform : EngineModule
options.SourceFiles.Add(Path.Combine(FolderPath, "Apple", "ApplePlatformSettings.h")); options.SourceFiles.Add(Path.Combine(FolderPath, "Apple", "ApplePlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "Mac", "MacPlatformSettings.h")); options.SourceFiles.Add(Path.Combine(FolderPath, "Mac", "MacPlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "iOS", "iOSPlatformSettings.h")); options.SourceFiles.Add(Path.Combine(FolderPath, "iOS", "iOSPlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "Web", "WebPlatformSettings.h"));
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform", "XboxOnePlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform", "XboxOnePlatformSettings.h"));
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform", "XboxScarlettPlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform", "XboxScarlettPlatformSettings.h"));
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS4", "Engine", "Platform", "PS4PlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS4", "Engine", "Platform", "PS4PlatformSettings.h"));

View File

@@ -32,6 +32,8 @@
#include "Mac/MacPlatform.h" #include "Mac/MacPlatform.h"
#elif PLATFORM_IOS #elif PLATFORM_IOS
#include "iOS/iOSPlatform.h" #include "iOS/iOSPlatform.h"
#elif PLATFORM_WEB
#include "Web/WebPlatform.h"
#else #else
#error Missing Platform implementation! #error Missing Platform implementation!
#endif #endif

View File

@@ -4,7 +4,7 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32ReadWriteLock.h" #include "Win32/Win32ReadWriteLock.h"
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS #elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS || PLATFORM_WEB
#include "Unix/UnixReadWriteLock.h" #include "Unix/UnixReadWriteLock.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchReadWriteLock.h" #include "Platforms/Switch/Engine/Platform/SwitchReadWriteLock.h"

View File

@@ -1,6 +1,5 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
#include "SDLInput.h"
#if PLATFORM_SDL && PLATFORM_MAC #if PLATFORM_SDL && PLATFORM_MAC
#include "SDLWindow.h" #include "SDLWindow.h"
@@ -21,10 +20,7 @@
#include "Engine/Platform/Unix/UnixFile.h" #include "Engine/Platform/Unix/UnixFile.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Platform/Apple/AppleUtils.h" #include "Engine/Platform/Linux/IncludeX11.h"
#include <Cocoa/Cocoa.h>
#include <AppKit/AppKit.h>
#include <QuartzCore/CAMetalLayer.h>
#include <SDL3/SDL_events.h> #include <SDL3/SDL_events.h>
#include <SDL3/SDL_hints.h> #include <SDL3/SDL_hints.h>
@@ -34,43 +30,6 @@
#include <SDL3/SDL_timer.h> #include <SDL3/SDL_timer.h>
#include <SDL3/SDL_video.h> #include <SDL3/SDL_video.h>
namespace MacImpl
{
Window* DraggedWindow = nullptr;
String DraggingData = String();
Float2 DraggingPosition;
Nullable<Float2> LastMouseDragPosition;
#if USE_EDITOR
//CriticalSection MacDragLocker;
bool DraggingActive = false;
bool DraggingIgnoreEvent = false;
NSDraggingSession* MacDragSession = nullptr;
//DoDragDropJob* MacDragJob = nullptr;
int64 MacDragExitFlag = 0;
#endif
}
class MacDropData : public IGuiData
{
public:
Type CurrentType;
String AsText;
Array<String> AsFiles;
Type GetType() const override
{
return CurrentType;
}
String GetAsText() const override
{
return AsText;
}
void GetAsFiles(Array<String>* files) const override
{
files->Add(AsFiles);
}
};
bool SDLPlatform::InitInternal() bool SDLPlatform::InitInternal()
{ {
return false; return false;
@@ -91,282 +50,16 @@ bool SDLPlatform::UsesX11()
return false; return false;
} }
bool SDLPlatform::EventFilterCallback(void* userdata, SDL_Event* event)
{
Window* draggedWindow = *(Window**)userdata;
if (draggedWindow == nullptr)
{
if (MacImpl::DraggingActive)
{
// Handle events during drag operation here since the normal event loop is blocked
if (event->type == SDL_EVENT_WINDOW_EXPOSED)
{
LOG(Info, "Window exposed event");
// The internal timer is sending exposed events every ~16ms
#if USE_EDITOR
// Flush any single-frame shapes to prevent memory leaking (eg. via terrain collision debug during scene drawing with PhysicsColliders or PhysicsDebug flag)
DebugDraw::UpdateContext(nullptr, 0.0f);
#endif
Engine::OnUpdate(); // For docking updates
Engine::OnDraw();
}
else
{
SDLWindow* window = SDLWindow::GetWindowFromEvent(*event);
if (window)
window->HandleEvent(*event);
// We do not receive events at steady rate to keep the engine updated...
#if USE_EDITOR
// Flush any single-frame shapes to prevent memory leaking (eg. via terrain collision debug during scene drawing with PhysicsColliders or PhysicsDebug flag)
DebugDraw::UpdateContext(nullptr, 0.0f);
#endif
Engine::OnUpdate(); // For docking updates
Engine::OnDraw();
if (event->type == SDL_EVENT_DROP_BEGIN || event->type == SDL_EVENT_DROP_FILE || event->type == SDL_EVENT_DROP_TEXT)
return true; // Filtering these event stops other following events from getting added to the queue
else
LOG(Info, "Unhandled event: {}", event->type);
}
return false;
}
return true;
}
return true;
// When the window is being dragged on Windows, the internal message loop is blocking
// the SDL event queue. We need to handle all relevant events in this event watch callback
// to ensure dragging related functionality doesn't break due to engine not getting updated.
// This also happens to fix the engine freezing during the dragging operation.
#if false
SDLWindow* window = SDLWindow::GetWindowFromEvent(*event);
if (event->type == SDL_EVENT_WINDOW_EXPOSED)
{
// The internal timer is sending exposed events every ~16ms
Engine::OnUpdate(); // For docking updates
Engine::OnDraw();
return false;
}
else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
{
if (window)
{
bool result = false;
window->OnLeftButtonHit(WindowHitCodes::Caption, result);
//if (result)
// return false;
window->HandleEvent(*event);
}
return false;
}
else if (event->type == SDL_EVENT_WINDOW_MOVED)
{
if (window)
window->HandleEvent(*event);
/*if (WinImpl::DraggedWindowSize != window->GetClientSize())
{
// The window size changed while dragging, most likely due to maximized window restoring back to previous size.
WinImpl::DraggedWindowMousePosition = WinImpl::DraggedWindowStartPosition + WinImpl::DraggedWindowMousePosition - window->GetClientPosition();
WinImpl::DraggedWindowStartPosition = window->GetClientPosition();
WinImpl::DraggedWindowSize = window->GetClientSize();
}
Float2 windowPosition = Float2(static_cast<float>(event->window.data1), static_cast<float>(event->window.data2));
Float2 mousePosition = WinImpl::DraggedWindowMousePosition;
// Generate mouse movement events while dragging the window around
SDL_Event mouseMovedEvent { 0 };
mouseMovedEvent.motion.type = SDL_EVENT_MOUSE_MOTION;
mouseMovedEvent.motion.windowID = SDL_GetWindowID(WinImpl::DraggedWindow->GetSDLWindow());
mouseMovedEvent.motion.timestamp = SDL_GetTicksNS();
mouseMovedEvent.motion.state = SDL_BUTTON_LEFT;
mouseMovedEvent.motion.x = mousePosition.X;
mouseMovedEvent.motion.y = mousePosition.Y;
if (window)
window->HandleEvent(mouseMovedEvent);*/
return false;
}
if (window)
window->HandleEvent(*event);
return false;
#endif
}
void SDLPlatform::PreHandleEvents() void SDLPlatform::PreHandleEvents()
{ {
SDL_SetEventFilter(EventFilterCallback, &MacImpl::DraggedWindow);
} }
void SDLPlatform::PostHandleEvents() void SDLPlatform::PostHandleEvents()
{ {
SDL_SetEventFilter(EventFilterCallback, &MacImpl::DraggedWindow);
// Handle window dragging release here
if (MacImpl::DraggedWindow != nullptr)
{
Float2 mousePosition;
auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
bool buttonReleased = (buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) == 0;
if (buttonReleased)
{
// Send simulated mouse up event
SDL_Event buttonUpEvent { 0 };
buttonUpEvent.motion.type = SDL_EVENT_MOUSE_BUTTON_UP;
buttonUpEvent.button.down = false;
buttonUpEvent.motion.windowID = SDL_GetWindowID(MacImpl::DraggedWindow->GetSDLWindow());
buttonUpEvent.motion.timestamp = SDL_GetTicksNS();
buttonUpEvent.motion.state = SDL_BUTTON_LEFT;
buttonUpEvent.button.clicks = 1;
buttonUpEvent.motion.x = mousePosition.X;
buttonUpEvent.motion.y = mousePosition.Y;
MacImpl::DraggedWindow->HandleEvent(buttonUpEvent);
MacImpl::DraggedWindow = nullptr;
}
}
} }
bool SDLWindow::HandleEventInternal(SDL_Event& event) bool SDLWindow::HandleEventInternal(SDL_Event& event)
{ {
switch (event.type)
{
case SDL_EVENT_WINDOW_MOVED:
{
// Quartz doesn't report any mouse events when mouse is over the caption area, send a simulated event instead...
Float2 mousePosition;
auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
if ((buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) != 0)
{
if (MacImpl::DraggedWindow == nullptr)
{
// TODO: verify mouse position, window focus
bool result = false;
OnLeftButtonHit(WindowHitCodes::Caption, result);
if (result)
MacImpl::DraggedWindow = this;
}
else
{
Float2 mousePos = Platform::GetMousePosition();
Input::Mouse->OnMouseMove(mousePos, this);
}
}
break;
}
case SDL_EVENT_MOUSE_BUTTON_UP:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
{
if (MacImpl::LastMouseDragPosition.HasValue())
{
// SDL reports wrong mouse position after dragging has ended
Float2 mouseClientPosition = ScreenToClient(MacImpl::LastMouseDragPosition.GetValue());
event.button.x = mouseClientPosition.X;
event.button.y = mouseClientPosition.Y;
}
break;
}
case SDL_EVENT_MOUSE_MOTION:
{
if (MacImpl::LastMouseDragPosition.HasValue())
MacImpl::LastMouseDragPosition.Reset();
if (MacImpl::DraggedWindow != nullptr)
return true;
break;
}
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
{
OnDragLeave(); // Check for release of mouse button too?
break;
}
//case SDL_EVENT_CLIPBOARD_UPDATE:
case SDL_EVENT_DROP_BEGIN:
case SDL_EVENT_DROP_POSITION:
case SDL_EVENT_DROP_FILE:
case SDL_EVENT_DROP_TEXT:
case SDL_EVENT_DROP_COMPLETE:
{
{
// HACK: We can't use Wayland listeners due to SDL also using them at the same time causes
// some of the events to drop and make it impossible to implement dragging on application side.
// We can get enough information through SDL_EVENT_DROP_* events to fill in the blanks for the
// drag and drop implementation.
auto dpiScale = GetDpiScale();
Float2 mousePos = Float2(event.drop.x * dpiScale, event.drop.y * dpiScale);
DragDropEffect effect = DragDropEffect::None;
String text(event.drop.data);
MacDropData dropData;
if (MacImpl::DraggingActive)
{
// We don't have the window dragging data during these events...
text = MacImpl::DraggingData;
mousePos = ScreenToClient(MacImpl::DraggingPosition);
// Ensure mouse position is updated while dragging
Input::Mouse->OnMouseMove(MacImpl::DraggingPosition, this);
MacImpl::LastMouseDragPosition = MacImpl::DraggingPosition;
}
dropData.AsText = text;
if (event.type == SDL_EVENT_DROP_BEGIN)
{
// We don't know the type of dragged data at this point, so call the events for both types
if (!MacImpl::DraggingActive)
{
dropData.CurrentType = IGuiData::Type::Files;
OnDragEnter(&dropData, mousePos, effect);
}
if (effect == DragDropEffect::None)
{
dropData.CurrentType = IGuiData::Type::Text;
OnDragEnter(&dropData, mousePos, effect);
}
}
else if (event.type == SDL_EVENT_DROP_POSITION)
{
Input::Mouse->OnMouseMove(ClientToScreen(mousePos), this);
// We don't know the type of dragged data at this point, so call the events for both types
if (!MacImpl::DraggingActive)
{
dropData.CurrentType = IGuiData::Type::Files;
OnDragOver(&dropData, mousePos, effect);
}
if (effect == DragDropEffect::None)
{
dropData.CurrentType = IGuiData::Type::Text;
OnDragOver(&dropData, mousePos, effect);
}
}
else if (event.type == SDL_EVENT_DROP_FILE)
{
text.Split('\n', dropData.AsFiles);
dropData.CurrentType = IGuiData::Type::Files;
OnDragDrop(&dropData, mousePos, effect);
}
else if (event.type == SDL_EVENT_DROP_TEXT)
{
dropData.CurrentType = IGuiData::Type::Text;
OnDragDrop(&dropData, mousePos, effect);
}
else if (event.type == SDL_EVENT_DROP_COMPLETE)
{
OnDragLeave();
if (MacImpl::DraggingActive)
{
// The previous drop events needs to be flushed to avoid processing them twice
SDL_FlushEvents(SDL_EVENT_DROP_FILE, SDL_EVENT_DROP_POSITION);
}
}
// TODO: Implement handling for feedback effect result (https://github.com/libsdl-org/SDL/issues/10448)
}
break;
}
}
return false; return false;
} }
@@ -375,207 +68,9 @@ void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
// TODO: This is now called before Platform::Init, ensure the scaling is changed accordingly during Platform::Init (see ApplePlatform::SetHighDpiAwarenessEnabled) // TODO: This is now called before Platform::Init, ensure the scaling is changed accordingly during Platform::Init (see ApplePlatform::SetHighDpiAwarenessEnabled)
} }
inline bool IsWindowInvalid(Window* win)
{
WindowsManager::WindowsLocker.Lock();
const bool hasWindow = WindowsManager::Windows.Contains(win);
WindowsManager::WindowsLocker.Unlock();
return !hasWindow || !win;
}
Float2 GetWindowTitleSize(const SDLWindow* window)
{
Float2 size = Float2::Zero;
if (window->GetSettings().HasBorder)
{
NSRect frameStart = [(NSWindow*)window->GetNativePtr() frameRectForContentRect:NSMakeRect(0, 0, 0, 0)];
size.Y = frameStart.size.height;
}
return size * MacPlatform::ScreenScale;
}
Float2 GetMousePosition(SDLWindow* window, NSEvent* event)
{
NSRect frame = [(NSWindow*)window->GetNativePtr() frame];
NSPoint point = [event locationInWindow];
return Float2(point.x, frame.size.height - point.y) * MacPlatform::ScreenScale - GetWindowTitleSize(window);
}
Float2 GetMousePosition(SDLWindow* window, const NSPoint& point)
{
NSRect frame = [(NSWindow*)window->GetNativePtr() frame];
CGRect screenBounds = CGDisplayBounds(CGMainDisplayID());
return Float2(point.x, screenBounds.size.height - point.y) * MacPlatform::ScreenScale;
}
void GetDragDropData(const SDLWindow* window, id<NSDraggingInfo> sender, Float2& mousePos, MacDropData& dropData)
{
NSRect frame = [(NSWindow*)window->GetNativePtr() frame];
NSPoint point = [sender draggingLocation];
mousePos = Float2(point.x, frame.size.height - point.y) * MacPlatform::ScreenScale - GetWindowTitleSize(window);
NSPasteboard* pasteboard = [sender draggingPasteboard];
if ([[pasteboard types] containsObject:NSPasteboardTypeString])
{
dropData.CurrentType = IGuiData::Type::Text;
dropData.AsText = AppleUtils::ToString((CFStringRef)[pasteboard stringForType:NSPasteboardTypeString]);
}
else
{
dropData.CurrentType = IGuiData::Type::Files;
NSArray* files = [pasteboard readObjectsForClasses:@[[NSURL class]] options:nil];
for (int32 i = 0; i < [files count]; i++)
{
NSString* url = [[files objectAtIndex:i] path];
NSString* file = [NSURL URLWithString:url].path;
dropData.AsFiles.Add(AppleUtils::ToString((CFStringRef)file));
}
}
}
NSDragOperation GetDragDropOperation(DragDropEffect dragDropEffect)
{
NSDragOperation result = NSDragOperationCopy;
switch (dragDropEffect)
{
case DragDropEffect::None:
//result = NSDragOperationNone;
break;
case DragDropEffect::Copy:
result = NSDragOperationCopy;
break;
case DragDropEffect::Move:
result = NSDragOperationMove;
break;
case DragDropEffect::Link:
result = NSDragOperationLink;
break;
}
return result;
}
#undef INCLUDED_IN_SDL
@interface ClipboardDataProviderImpl : NSObject <NSPasteboardItemDataProvider, NSDraggingSource>
{
@public
SDLWindow* Window;
}
@end
@implementation ClipboardDataProviderImpl
// NSPasteboardItemDataProvider
// ---
- (void)pasteboard:(nullable NSPasteboard*)pasteboard item:(NSPasteboardItem*)item provideDataForType:(NSPasteboardType)type
{
LOG(Info, "pasteboard");
if (IsWindowInvalid(Window)) return;
[pasteboard setString:(NSString*)AppleUtils::ToString(MacImpl::DraggingData) forType:NSPasteboardTypeString];
}
- (void)pasteboardFinishedWithDataProvider:(NSPasteboard*)pasteboard
{
LOG(Info, "pasteboardFinishedWithDataProvider");
}
// NSDraggingSource
// ---
- (NSDragOperation)draggingSession:(NSDraggingSession*)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
if (IsWindowInvalid(Window))
return NSDragOperationNone;
switch(context)
{
case NSDraggingContextOutsideApplication:
LOG(Info, "draggingSession sourceOperationMaskForDraggingContext: outside");
return NSDragOperationCopy;
case NSDraggingContextWithinApplication:
LOG(Info, "draggingSession sourceOperationMaskForDraggingContext: inside");
return NSDragOperationCopy;
default:
LOG(Info, "draggingSession sourceOperationMaskForDraggingContext: unknown");
return NSDragOperationMove;
}
}
- (void)draggingSession:(NSDraggingSession*)session willBeginAtPoint:(NSPoint)screenPoint
{
LOG(Info, "draggingSession willBeginAtPoint");
MacImpl::DraggingPosition = GetMousePosition(Window, screenPoint);
}
- (void)draggingSession:(NSDraggingSession*)session movedToPoint:(NSPoint)screenPoint
{
//LOG(Info, "draggingSession movedToPoint");
MacImpl::DraggingPosition = GetMousePosition(Window, screenPoint);
}
- (void)draggingSession:(NSDraggingSession*)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
{
LOG(Info, "draggingSession endedAtPoint");
MacImpl::DraggingPosition = GetMousePosition(Window, screenPoint);
#if USE_EDITOR
// Stop background worker once the drag ended
if (MacImpl::MacDragSession && MacImpl::MacDragSession == session)
Platform::AtomicStore(&MacImpl::MacDragExitFlag, 1);
#endif
}
@end
DragDropEffect SDLWindow::DoDragDrop(const StringView& data) DragDropEffect SDLWindow::DoDragDrop(const StringView& data)
{ {
NSWindow* window = (NSWindow*)_handle; return DragDropEffect::None;
ClipboardDataProviderImpl* clipboardDataProvider = [ClipboardDataProviderImpl alloc];
clipboardDataProvider->Window = this;
// Create mouse drag event
NSEvent* event = [NSEvent
mouseEventWithType:NSEventTypeLeftMouseDragged
location:window.mouseLocationOutsideOfEventStream
modifierFlags:0
timestamp:NSApp.currentEvent.timestamp
windowNumber:window.windowNumber
context:nil
eventNumber:0
clickCount:1
pressure:1.0];
// Create drag item
NSPasteboardItem* pasteItem = [NSPasteboardItem new];
[pasteItem setDataProvider:clipboardDataProvider forTypes:[NSArray arrayWithObjects:NSPasteboardTypeString, nil]];
NSDraggingItem* dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pasteItem];
[dragItem setDraggingFrame:NSMakeRect(event.locationInWindow.x, event.locationInWindow.y, 100, 100) contents:nil];
// Start dragging session
NSDraggingSession* draggingSession = [window.contentView beginDraggingSessionWithItems:[NSArray arrayWithObject:dragItem] event:event source:clipboardDataProvider];
DragDropEffect result = DragDropEffect::None;
#if USE_EDITOR
// Create background worker that will keep updating GUI (perform rendering)
ASSERT(!MacImpl::MacDragSession);
MacImpl::MacDragSession = draggingSession;
MacImpl::MacDragExitFlag = 0;
MacImpl::DraggingData = data;
MacImpl::DraggingActive = true;
while (Platform::AtomicRead(&MacImpl::MacDragExitFlag) == 0)
{
// The internal event loop will block here during the drag operation,
// events are processed in the event filter callback instead.
SDLPlatform::Tick();
Platform::Sleep(1);
}
MacImpl::DraggingActive = false;
MacImpl::DraggingData.Clear();
MacImpl::MacDragSession = nullptr;
#endif
return result;
} }
DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow) DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)

View File

@@ -0,0 +1,207 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#if PLATFORM_SDL && PLATFORM_WEB
#include "SDLWindow.h"
#include "Engine/Platform/MessageBox.h"
#include "Engine/Core/Log.h"
#include <SDL3/SDL_messagebox.h>
#include <SDL3/SDL_error.h>
bool SDLPlatform::InitInternal()
{
return false;
}
bool SDLPlatform::UsesWindows()
{
return false;
}
bool SDLPlatform::UsesWayland()
{
return false;
}
bool SDLPlatform::UsesX11()
{
return false;
}
void SDLPlatform::PreHandleEvents()
{
}
void SDLPlatform::PostHandleEvents()
{
}
bool SDLWindow::HandleEventInternal(SDL_Event& event)
{
return false;
}
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
{
}
DragDropEffect SDLWindow::DoDragDrop(const StringView& data)
{
return DragDropEffect::None;
}
DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
{
Show();
return DragDropEffect::None;
}
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
StringAnsi textAnsi(text);
StringAnsi captionAnsi(caption);
SDL_MessageBoxData data;
SDL_MessageBoxButtonData dataButtons[3];
data.window = nullptr;
data.title = captionAnsi.GetText();
data.message = textAnsi.GetText();
data.colorScheme = nullptr;
switch (icon)
{
case MessageBoxIcon::Error:
case MessageBoxIcon::Hand:
case MessageBoxIcon::Stop:
data.flags |= SDL_MESSAGEBOX_ERROR;
break;
case MessageBoxIcon::Asterisk:
case MessageBoxIcon::Information:
case MessageBoxIcon::Question:
data.flags |= SDL_MESSAGEBOX_INFORMATION;
break;
case MessageBoxIcon::Exclamation:
case MessageBoxIcon::Warning:
data.flags |= SDL_MESSAGEBOX_WARNING;
break;
default:
break;
}
switch (buttons)
{
case MessageBoxButtons::AbortRetryIgnore:
dataButtons[0] =
{
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
(int)DialogResult::Abort,
"Abort"
};
dataButtons[1] =
{
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
(int)DialogResult::Retry,
"Retry"
};
dataButtons[2] =
{
0,
(int)DialogResult::Ignore,
"Ignore"
};
data.numbuttons = 3;
break;
case MessageBoxButtons::OK:
dataButtons[0] =
{
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT | SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
(int)DialogResult::OK,
"OK"
};
data.numbuttons = 1;
break;
case MessageBoxButtons::OKCancel:
dataButtons[0] =
{
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
(int)DialogResult::OK,
"OK"
};
dataButtons[1] =
{
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
(int)DialogResult::Cancel,
"Cancel"
};
data.numbuttons = 2;
break;
case MessageBoxButtons::RetryCancel:
dataButtons[0] =
{
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
(int)DialogResult::Retry,
"Retry"
};
dataButtons[1] =
{
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
(int)DialogResult::Cancel,
"Cancel"
};
data.numbuttons = 2;
break;
case MessageBoxButtons::YesNo:
dataButtons[0] =
{
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
(int)DialogResult::Yes,
"Yes"
};
dataButtons[1] =
{
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
(int)DialogResult::No,
"No"
};
data.numbuttons = 2;
break;
case MessageBoxButtons::YesNoCancel:
{
dataButtons[0] =
{
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
(int)DialogResult::Yes,
"Yes"
};
dataButtons[1] =
{
0,
(int)DialogResult::No,
"No"
};
dataButtons[2] =
{
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
(int)DialogResult::Cancel,
"Cancel"
};
data.numbuttons = 3;
break;
}
default:
break;
}
data.buttons = dataButtons;
int result = -1;
if (!SDL_ShowMessageBox(&data, &result))
{
LOG(Error, "Failed to show SDL message box: {0}", String(SDL_GetError()));
return DialogResult::Abort;
}
if (result < 0)
return DialogResult::None;
return (DialogResult)result;
}
#endif

View File

@@ -32,7 +32,7 @@
namespace SDLImpl namespace SDLImpl
{ {
int32 SystemDpi = 96; int32 SystemDpi = 96;
#if PLATFORM_LINUX #if PLATFORM_LINUX || PLATFORM_WEB
String UserLocale("en"); String UserLocale("en");
#endif #endif
bool WindowDecorationsSupported = true; bool WindowDecorationsSupported = true;
@@ -109,7 +109,7 @@ bool SDLPlatform::Init()
if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
Platform::Fatal(String::Format(TEXT("Failed to initialize SDL: {0}."), String(SDL_GetError()))); Platform::Fatal(String::Format(TEXT("Failed to initialize SDL: {0}."), String(SDL_GetError())));
#if PLATFORM_LINUX #if PLATFORM_LINUX || PLATFORM_WEB
int localesCount = 0; int localesCount = 0;
auto locales = SDL_GetPreferredLocales(&localesCount); auto locales = SDL_GetPreferredLocales(&localesCount);
for (int i = 0; i < localesCount; i++) for (int i = 0; i < localesCount; i++)
@@ -241,7 +241,7 @@ int32 SDLPlatform::GetDpi()
return SDLImpl::SystemDpi; return SDLImpl::SystemDpi;
} }
#if PLATFORM_LINUX #if PLATFORM_LINUX || PLATFORM_WEB
String SDLPlatform::GetUserLocaleName() String SDLPlatform::GetUserLocaleName()
{ {
return SDLImpl::UserLocale; return SDLImpl::UserLocale;
@@ -435,8 +435,10 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd.Get()); SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd.Get());
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env); SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env);
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, background); SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, background);
#if !PLATFORM_WEB
if (workingDirectory.HasChars()) if (workingDirectory.HasChars())
SDL_SetStringProperty(props, SDL_PROP_PROCESS_CREATE_WORKING_DIRECTORY_STRING, workingDirectory.Get()); SDL_SetStringProperty(props, SDL_PROP_PROCESS_CREATE_WORKING_DIRECTORY_STRING, workingDirectory.Get());
#endif
if (captureStdOut) if (captureStdOut)
{ {
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP); SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);

View File

@@ -7,29 +7,25 @@
#include "Engine/Platform/Base/Enums.h" #include "Engine/Platform/Base/Enums.h"
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
#include "Engine/Platform/Windows/WindowsPlatform.h" #include "Engine/Platform/Windows/WindowsPlatform.h"
typedef WindowsPlatform SDLPlatformBase;
typedef struct tagMSG MSG; typedef struct tagMSG MSG;
#elif PLATFORM_LINUX #elif PLATFORM_LINUX
#include "Engine/Platform/Linux/LinuxPlatform.h" #include "Engine/Platform/Linux/LinuxPlatform.h"
typedef LinuxPlatform SDLPlatformBase;
union _XEvent; union _XEvent;
#elif PLATFORM_MAC #elif PLATFORM_MAC
#include "Engine/Platform/Mac/MacPlatform.h" #include "Engine/Platform/Mac/MacPlatform.h"
typedef MacPlatform SDLPlatformBase;
#elif PLATFORM_WEB
#include "Engine/Platform/Web/WebPlatform.h"
typedef WebPlatform SDLPlatformBase;
#else #else
static_assert(false, "Unsupported Platform"); static_assert(false, "Unsupported SDL platform.");
#endif #endif
class SDLWindow; class SDLWindow;
union SDL_Event; union SDL_Event;
#if PLATFORM_WINDOWS
typedef WindowsPlatform SDLPlatformBase;
#elif PLATFORM_LINUX
typedef LinuxPlatform SDLPlatformBase;
#elif PLATFORM_MAC
typedef MacPlatform SDLPlatformBase;
#else
static_assert(false, "Unsupported SDL platform.");
#endif
/// <summary> /// <summary>
/// The SDL platform implementation and application management utilities. /// The SDL platform implementation and application management utilities.
/// </summary> /// </summary>
@@ -84,7 +80,7 @@ public:
static BatteryInfo GetBatteryInfo(); static BatteryInfo GetBatteryInfo();
#endif #endif
static int32 GetDpi(); static int32 GetDpi();
#if PLATFORM_LINUX #if PLATFORM_LINUX || PLATFORM_WEB
static String GetUserLocaleName(); static String GetUserLocaleName();
#endif #endif
static bool CanOpenUrl(const StringView& url); static bool CanOpenUrl(const StringView& url);

View File

@@ -38,6 +38,7 @@
#include "Engine/Platform/Linux/IncludeX11.h" #include "Engine/Platform/Linux/IncludeX11.h"
#elif PLATFORM_MAC #elif PLATFORM_MAC
#include <Cocoa/Cocoa.h> #include <Cocoa/Cocoa.h>
#elif PLATFORM_WEB
#else #else
static_assert(false, "Unsupported Platform"); static_assert(false, "Unsupported Platform");
#endif #endif
@@ -77,6 +78,8 @@ void* GetNativeWindowPointer(SDL_Window* window)
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, nullptr); windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, nullptr);
#elif PLATFORM_IOS #elif PLATFORM_IOS
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr); windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr);
#elif PLATFORM_WEB
windowPtr = (void*)1; // Mock value (TODO: consider SDL_PROP_WINDOW_EMSCRIPTEN_CANVAS_ID_STRING)
#else #else
static_assert(false, "unsupported platform"); static_assert(false, "unsupported platform");
#endif #endif

View File

@@ -16,6 +16,8 @@
#include "Platforms/Switch/Engine/Platform/SwitchThread.h" #include "Platforms/Switch/Engine/Platform/SwitchThread.h"
#elif PLATFORM_MAC || PLATFORM_IOS #elif PLATFORM_MAC || PLATFORM_IOS
#include "Apple/AppleThread.h" #include "Apple/AppleThread.h"
#elif PLATFORM_WEB
#include "Web/WebThread.h"
#else #else
#error Missing Thread implementation! #error Missing Thread implementation!
#endif #endif

View File

@@ -305,6 +305,29 @@ typedef UnixNetwork Network;
class UserBase; class UserBase;
typedef UserBase User; typedef UserBase User;
#elif PLATFORM_WEB
class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixReadWriteLock;
typedef UnixReadWriteLock ReadWriteLock;
class UnixConditionVariable;
typedef UnixConditionVariable ConditionVariable;
class WebFileSystem;
typedef WebFileSystem FileSystem;
class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class UnixFile;
typedef UnixFile File;
class WebThread;
typedef WebThread Thread;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class UnixNetwork;
typedef UnixNetwork Network;
class UserBase;
typedef UserBase User;
#else #else
#error Missing Types implementation! #error Missing Types implementation!

View File

@@ -1,6 +1,6 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
#if PLATFORM_UNIX #if PLATFORM_UNIX && !PLATFORM_WEB
#include "UnixThread.h" #include "UnixThread.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"

View File

@@ -2,7 +2,7 @@
#pragma once #pragma once
#if PLATFORM_UNIX #if PLATFORM_UNIX && !PLATFORM_WEB
#include "Engine/Platform/Base/ThreadBase.h" #include "Engine/Platform/Base/ThreadBase.h"
#include <pthread.h> #include <pthread.h>

View File

@@ -0,0 +1,30 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_WEB
#include "../Unix/UnixDefines.h"
// Platform description
#define PLATFORM_TYPE PlatformType::Web
#define PLATFORM_64BITS 0
#define PLATFORM_ARCH ArchitectureType::x86
#define PLATFORM_CACHE_LINE_SIZE 64
#define PLATFORM_DEBUG_BREAK
#define PLATFORM_OUT_OF_MEMORY_BUFFER_SIZE 0
// Configure graphics
#define GPU_ALLOW_TESSELLATION_SHADERS 0
#define GPU_ALLOW_GEOMETRY_SHADERS 0
#define GPU_ALLOW_PROFILE_EVENTS 0
// Threading is optional
#ifdef __EMSCRIPTEN_PTHREADS__
#define PLATFORM_THREADS_LIMIT 4
#else
#define PLATFORM_THREADS_LIMIT 1
#define GPU_ENABLE_ASYNC_RESOURCES_CREATION 0
#endif
#endif

View File

@@ -0,0 +1,19 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_WEB
#include "Engine/Platform/Unix/UnixFileSystem.h"
/// <summary>
/// Web platform implementation of filesystem service.
/// </summary>
class FLAXENGINE_API WebFileSystem : public UnixFileSystem
{
public:
// [UnixFileSystem]
static void GetSpecialFolderPath(const SpecialFolder type, String& result);
};
#endif

View File

@@ -0,0 +1,295 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#if PLATFORM_WEB
#include "WebPlatform.h"
#include "WebFileSystem.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/Version.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Platform/CPUInfo.h"
#include "Engine/Platform/MemoryStats.h"
#if !BUILD_RELEASE
#include "Engine/Core/Types/StringView.h"
#include "Engine/Utilities/StringConverter.h"
#endif
#include <chrono>
#include <unistd.h>
#include <emscripten/emscripten.h>
#include <emscripten/threading.h>
#include <emscripten/version.h>
#include <emscripten/heap.h>
namespace
{
CPUInfo Cpu;
};
void WebFileSystem::GetSpecialFolderPath(const SpecialFolder type, String& result)
{
result = TEXT("/");
}
String WebPlatform::GetSystemName()
{
return TEXT("Browser");
}
Version WebPlatform::GetSystemVersion()
{
return Version(1, 0);
}
CPUInfo WebPlatform::GetCPUInfo()
{
return Cpu;
}
MemoryStats WebPlatform::GetMemoryStats()
{
// Mock memory stats
MemoryStats result;
result.TotalPhysicalMemory = emscripten_get_heap_max();
result.UsedPhysicalMemory = emscripten_get_heap_size();
result.TotalVirtualMemory = 2ull * 1024 * 1024 * 1024; // Max 2GB
result.UsedVirtualMemory = result.UsedPhysicalMemory;
result.ProgramSizeMemory = 0;
return result;
}
ProcessMemoryStats WebPlatform::GetProcessMemoryStats()
{
// Mock memory stats
ProcessMemoryStats result;
result.UsedPhysicalMemory = 1 * 1024 * 1024;
result.UsedVirtualMemory = result.UsedPhysicalMemory;
return result;
}
void WebPlatform::SetThreadPriority(ThreadPriority priority)
{
// Not supported
}
void WebPlatform::SetThreadAffinityMask(uint64 affinityMask)
{
// Not supported
}
void WebPlatform::Sleep(int32 milliseconds)
{
//emscripten_sleep(milliseconds);
emscripten_thread_sleep(milliseconds);
}
void WebPlatform::Yield()
{
Sleep(0);
}
double WebPlatform::GetTimeSeconds()
{
double time = emscripten_get_now();
return time * 0.001;
}
uint64 WebPlatform::GetTimeCycles()
{
return (uint64)(emscripten_get_now() * 1000.0);
}
void WebPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
{
// Get local time
using namespace std::chrono;
system_clock::time_point now = system_clock::now();
time_t tt = system_clock::to_time_t(now);
tm time = *localtime(&tt);
// Extract time
year = time.tm_year + 1900;
month = time.tm_mon + 1;
dayOfWeek = time.tm_wday;
day = time.tm_mday;
hour = time.tm_hour;
minute = time.tm_min;
second = time.tm_sec;
millisecond = (int64)emscripten_get_now() % 1000; // Fake it based on other timer
}
void WebPlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
{
// Get UTC time
using namespace std::chrono;
system_clock::time_point now = system_clock::now();
time_t tt = system_clock::to_time_t(now);
tm time = *gmtime(&tt);
// Extract time
year = time.tm_year + 1900;
month = time.tm_mon + 1;
dayOfWeek = time.tm_wday;
day = time.tm_mday;
hour = time.tm_hour;
minute = time.tm_min;
second = time.tm_sec;
millisecond = (int64)emscripten_get_now() % 1000; // Fake it based on other timer
}
#if !BUILD_RELEASE
void WebPlatform::Log(const StringView& msg)
{
const StringAsANSI<512> msgAnsi(*msg, msg.Length());
// Fix % characters that should not be formatted
auto buffer = (char*)msgAnsi.Get();
for (int32 i = 0; buffer[i]; i++)
{
if (buffer[i] == '%')
buffer[i] = 'p';
}
emscripten_log(EM_LOG_CONSOLE, buffer);
}
bool WebPlatform::IsDebuggerPresent()
{
return false;
}
#endif
String WebPlatform::GetComputerName()
{
return TEXT("Web");
}
bool WebPlatform::GetHasFocus()
{
return true;
}
String WebPlatform::GetMainDirectory()
{
return TEXT("/");
}
String WebPlatform::GetExecutableFilePath()
{
return TEXT("/index.html");
}
Guid WebPlatform::GetUniqueDeviceId()
{
return Guid(1, 2, 3, 4);
}
String WebPlatform::GetWorkingDirectory()
{
return GetMainDirectory();
}
bool WebPlatform::SetWorkingDirectory(const String& path)
{
return true;
}
bool WebPlatform::Init()
{
if (PlatformBase::Init())
return true;
// Set info about the CPU
Platform::MemoryClear(&Cpu, sizeof(Cpu));
Cpu.ProcessorPackageCount = 1;
Cpu.ProcessorCoreCount = Math::Min(emscripten_num_logical_cores(), PLATFORM_THREADS_LIMIT);
Cpu.LogicalProcessorCount = Cpu.ProcessorCoreCount;
Cpu.ClockSpeed = GetClockFrequency();
return false;
}
void WebPlatform::LogInfo()
{
PlatformBase::LogInfo();
#ifdef __EMSCRIPTEN_major__
LOG(Info, "Emscripten {}.{}.{}", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
#elif defined(__EMSCRIPTEN_MAJOR__)
LOG(Info, "Emscripten {}.{}.{}", __EMSCRIPTEN_MAJOR__, __EMSCRIPTEN_MINOR__, __EMSCRIPTEN_TINY__);
#else
LOG(Info, "Emscripten");
#endif
#ifdef __EMSCRIPTEN_PTHREADS__
LOG(Info, "Threading: pthreads");
#else
LOG(Info, "Threading: disabled");
#endif
}
void WebPlatform::Tick()
{
}
void WebPlatform::Exit()
{
}
extern char** environ;
void WebPlatform::GetEnvironmentVariables(Dictionary<String, String, HeapAllocation>& result)
{
char** s = environ;
for (; *s; s++)
{
char* var = *s;
int32 split = -1;
for (int32 i = 0; var[i]; i++)
{
if (var[i] == '=')
{
split = i;
break;
}
}
if (split == -1)
result[String(var)] = String::Empty;
else
result[String(var, split)] = String(var + split + 1);
}
}
bool WebPlatform::GetEnvironmentVariable(const String& name, String& value)
{
char* env = getenv(StringAsANSI<>(*name).Get());
if (env)
{
value = String(env);
return false;
}
return true;
}
bool WebPlatform::SetEnvironmentVariable(const String& name, const String& value)
{
return setenv(StringAsANSI<>(*name).Get(), StringAsANSI<>(*value).Get(), true) != 0;
}
void* WebPlatform::LoadLibrary(const Char* filename)
{
return nullptr;
}
void WebPlatform::FreeLibrary(void* handle)
{
}
void* WebPlatform::GetProcAddress(void* handle, const char* symbol)
{
return nullptr;
}
#endif

View File

@@ -0,0 +1,119 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_WEB
#include "../Unix/UnixPlatform.h"
#ifdef __EMSCRIPTEN_PTHREADS__
#include <pthread.h>
#endif
/// <summary>
/// The Web platform implementation and application management utilities.
/// </summary>
class FLAXENGINE_API WebPlatform : public UnixPlatform
{
public:
// [UnixPlatform]
FORCE_INLINE static void MemoryBarrier()
{
#ifdef __EMSCRIPTEN_PTHREADS__
// Fake a fence with an arbitrary atomic operation (from emscripten_atomic_fence to avoid including it for less header bloat)
uint8 temp = 0;
__c11_atomic_fetch_or((_Atomic uint8*)&temp, 0, __ATOMIC_SEQ_CST);
#endif
}
FORCE_INLINE static void MemoryPrefetch(void const* ptr)
{
__builtin_prefetch(static_cast<char const*>(ptr));
}
FORCE_INLINE static int64 InterlockedExchange(int64 volatile* dst, int64 exchange)
{
return __sync_lock_test_and_set(dst, exchange);
}
FORCE_INLINE static int32 InterlockedCompareExchange(int32 volatile* dst, int32 exchange, int32 comperand)
{
return __sync_val_compare_and_swap(dst, comperand, exchange);
}
FORCE_INLINE static int64 InterlockedCompareExchange(int64 volatile* dst, int64 exchange, int64 comperand)
{
return __sync_val_compare_and_swap(dst, comperand, exchange);
}
FORCE_INLINE static int64 InterlockedIncrement(int64 volatile* dst)
{
return __sync_add_and_fetch(dst, 1);
}
FORCE_INLINE static int64 InterlockedDecrement(int64 volatile* dst)
{
return __sync_sub_and_fetch(dst, 1);
}
FORCE_INLINE static int64 InterlockedAdd(int64 volatile* dst, int64 value)
{
return __sync_fetch_and_add(dst, value);
}
FORCE_INLINE static int32 AtomicRead(int32 const volatile* dst)
{
int32 result;
__atomic_load(dst, &result, __ATOMIC_SEQ_CST);
return result;
}
FORCE_INLINE static int64 AtomicRead(int64 const volatile* dst)
{
int64 result;
__atomic_load(dst, &result, __ATOMIC_SEQ_CST);
return result;
}
FORCE_INLINE static void AtomicStore(int32 volatile* dst, int32 value)
{
__atomic_store(dst, &value, __ATOMIC_SEQ_CST);
}
FORCE_INLINE static void AtomicStore(int64 volatile* dst, int64 value)
{
__atomic_store(dst, &value, __ATOMIC_SEQ_CST);
}
FORCE_INLINE static uint64 GetCurrentThreadID()
{
#ifdef __EMSCRIPTEN_PTHREADS__
return (uint64)pthread_self();
#else
return 1;
#endif
}
static String GetSystemName();
static Version GetSystemVersion();
static CPUInfo GetCPUInfo();
static MemoryStats GetMemoryStats();
static ProcessMemoryStats GetProcessMemoryStats();
static void SetThreadPriority(ThreadPriority priority);
static void SetThreadAffinityMask(uint64 affinityMask);
static void Sleep(int32 milliseconds);
static void Yield();
static double GetTimeSeconds();
static uint64 GetTimeCycles();
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
#if !BUILD_RELEASE
static void Log(const StringView& msg);
static bool IsDebuggerPresent();
#endif
static String GetComputerName();
static bool GetHasFocus();
static String GetMainDirectory();
static String GetExecutableFilePath();
static Guid GetUniqueDeviceId();
static String GetWorkingDirectory();
static bool SetWorkingDirectory(const String& path);
static bool Init();
static void LogInfo();
static void Tick();
static void Exit();
static void GetEnvironmentVariables(Dictionary<String, String, HeapAllocation>& result);
static bool GetEnvironmentVariable(const String& name, String& value);
static bool SetEnvironmentVariable(const String& name, const String& value);
static void* LoadLibrary(const Char* filename);
static void FreeLibrary(void* handle);
static void* GetProcAddress(void* handle, const char* symbol);
};
#endif

View File

@@ -0,0 +1,27 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_WEB || USE_EDITOR
#include "Engine/Core/Config/PlatformSettingsBase.h"
/// <summary>
/// Web platform settings.
/// </summary>
API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API WebPlatformSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(WebPlatformSettings);
API_AUTO_SERIALIZATION();
/// <summary>
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
/// </summary>
static WebPlatformSettings* Get();
};
#if PLATFORM_WEB
typedef WebPlatformSettings PlatformSettings;
#endif
#endif

View File

@@ -0,0 +1,63 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_WEB
#include "../Base/ThreadBase.h"
/// <summary>
/// Thread object for Web platform.
/// </summary>
class FLAXENGINE_API WebThread : public ThreadBase
{
public:
/// <summary>
/// Initializes a new instance of the <see cref="WebThreadThread"/> class.
/// </summary>
/// <param name="runnable">The runnable.</param>
/// <param name="name">The thread name.</param>
/// <param name="priority">The thread priority.</param>
WebThread(IRunnable* runnable, const String& name, ThreadPriority priority)
: ThreadBase(runnable, name, priority)
{
}
public:
/// <summary>
/// Factory method to create a thread with the specified stack size and thread priority
/// </summary>
/// <param name="runnable">The runnable object to execute</param>
/// <param name="name">Name of the thread</param>
/// <param name="priority">Tells the thread whether it needs to adjust its priority or not. Defaults to normal priority</param>
/// <param name="stackSize">The size of the stack to create. 0 means use the current thread's stack size</param>
/// <returns>Pointer to the new thread or null if cannot create it</returns>
static WebThread* Create(IRunnable* runnable, const String& name, ThreadPriority priority = ThreadPriority::Normal, uint32 stackSize = 0)
{
return New<WebThread>(runnable, name, priority);
}
public:
// [ThreadBase]
void Join() override
{
// TOOD: impl this
}
protected:
// [ThreadBase]
void ClearHandleInternal() override
{
// TOOD: impl this
}
void SetPriorityInternal(ThreadPriority priority) override
{
// TOOD: impl this
}
void KillInternal(bool waitForJoin) override
{
// TOOD: impl this
}
};
#endif

View File

@@ -11,7 +11,7 @@
/// </summary> /// </summary>
API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API iOSPlatformSettings : public ApplePlatformSettings API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API iOSPlatformSettings : public ApplePlatformSettings
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(ApplePlatformSettings); DECLARE_SCRIPTING_TYPE_MINIMAL(iOSPlatformSettings);
API_AUTO_SERIALIZATION(); API_AUTO_SERIALIZATION();
/// <summary> /// <summary>

View File

@@ -15,6 +15,8 @@ public class Profiler : EngineModule
/// <returns>True if use profiler, otherwise false.</returns> /// <returns>True if use profiler, otherwise false.</returns>
public static bool Use(BuildOptions options) public static bool Use(BuildOptions options)
{ {
if (options.Platform.Target == TargetPlatform.Web)
return false;
return options.Configuration != TargetConfiguration.Release || options.Target.IsEditor; return options.Configuration != TargetConfiguration.Release || options.Target.IsEditor;
} }

View File

@@ -304,7 +304,7 @@ GPUTexture* ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPU
// Also for high surface roughness values it adds more blur to the reflection tail which looks more realistic. // Also for high surface roughness values it adds more blur to the reflection tail which looks more realistic.
// Downscale with gaussian blur // Downscale with gaussian blur
auto filterMode = PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? MultiScaler::FilterMode::GaussianBlur5 : MultiScaler::FilterMode::GaussianBlur9; auto filterMode = PLATFORM_WEB || PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? MultiScaler::FilterMode::GaussianBlur5 : MultiScaler::FilterMode::GaussianBlur9;
for (int32 mipLevel = 1; mipLevel < colorBufferMips; mipLevel++) for (int32 mipLevel = 1; mipLevel < colorBufferMips; mipLevel++)
{ {
const int32 mipWidth = Math::Max(colorBufferWidth >> mipLevel, 1); const int32 mipWidth = Math::Max(colorBufferWidth >> mipLevel, 1);

View File

@@ -784,7 +784,7 @@ void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render
// Disable cascades blending when baking lightmaps // Disable cascades blending when baking lightmaps
if (IsRunningRadiancePass) if (IsRunningRadiancePass)
atlasLight.BlendCSM = false; atlasLight.BlendCSM = false;
#elif PLATFORM_SWITCH || PLATFORM_IOS || PLATFORM_ANDROID #elif PLATFORM_WEB || PLATFORM_SWITCH || PLATFORM_IOS || PLATFORM_ANDROID
// Disable cascades blending on low-end platforms // Disable cascades blending on low-end platforms
atlasLight.BlendCSM = false; atlasLight.BlendCSM = false;
#endif #endif

View File

@@ -573,6 +573,7 @@ bool Scripting::Load()
auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine(); auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine();
if (!flaxEngineModule->Assembly->IsLoaded()) if (!flaxEngineModule->Assembly->IsLoaded())
{ {
#if USE_CSHARP
String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
#if USE_MONO_AOT #if USE_MONO_AOT
if (!FileSystem::FileExists(flaxEnginePath)) if (!FileSystem::FileExists(flaxEnginePath))
@@ -583,6 +584,7 @@ bool Scripting::Load()
LOG(Error, "Failed to load FlaxEngine C# assembly."); LOG(Error, "Failed to load FlaxEngine C# assembly.");
return true; return true;
} }
#endif
flaxEngineModule->CanReload = false; flaxEngineModule->CanReload = false;
flaxEngineModule->Assembly->_canReload = false; flaxEngineModule->Assembly->_canReload = false;
onEngineLoaded(flaxEngineModule->Assembly); onEngineLoaded(flaxEngineModule->Assembly);

View File

@@ -3,7 +3,6 @@
#include "JsonTools.h" #include "JsonTools.h"
#include "ISerializable.h" #include "ISerializable.h"
#include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Core/Types/CommonValue.h"
#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/DateTime.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ScriptingObjectReference.h" #include "Engine/Scripting/ScriptingObjectReference.h"
@@ -284,87 +283,3 @@ DateTime JsonTools::GetDateTime(const Value& value)
{ {
return DateTime(value.GetInt64()); return DateTime(value.GetInt64());
} }
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#include "Engine/Content/Deprecated.h"
CommonValue JsonTools::GetCommonValue(const Value& value)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
MARK_CONTENT_DEPRECATED();
CommonValue result;
const auto typeMember = value.FindMember("Type");
const auto valueMember = value.FindMember("Value");
if (typeMember != value.MemberEnd() && typeMember->value.IsInt() && valueMember != value.MemberEnd())
{
const auto& v = valueMember->value;
switch ((CommonType)typeMember->value.GetInt())
{
case CommonType::Bool:
result = v.GetBool();
break;
case CommonType::Integer:
result = v.GetInt();
break;
case CommonType::Float:
result = v.GetFloat();
break;
case CommonType::Vector2:
result = GetFloat2(v);
break;
case CommonType::Vector3:
result = GetFloat3(v);
break;
case CommonType::Vector4:
result = GetFloat4(v);
break;
case CommonType::Color:
result = GetColor(v);
break;
case CommonType::Guid:
result = GetGuid(v);
break;
case CommonType::String:
result = v.GetText();
break;
case CommonType::Box:
result = GetBoundingBox(v);
break;
case CommonType::Rotation:
result = GetQuaternion(v);
break;
case CommonType::Transform:
result = GetTransform(v);
break;
case CommonType::Sphere:
result = GetBoundingSphere(v);
break;
case CommonType::Rectangle:
result = GetRectangle(v);
break;
case CommonType::Ray:
result = GetRay(v);
break;
case CommonType::Pointer:
result = (void*)v.GetInt64();
break;
case CommonType::Matrix:
result = GetMatrix(v);
break;
case CommonType::Blob:
{
const int32 length = v.GetStringLength();
result.SetBlob(length);
Encryption::Base64Decode(v.GetString(), length, result.AsBlob.Data);
break;
}
case CommonType::Object:
result = FindObject(GetGuid(v), ScriptingObject::GetStaticClass());
break;
default:
CRASH;
break;
}
}
return result;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS

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