Refactor settings types to use scripting API

This commit is contained in:
Wojtek Figat
2021-01-05 14:14:34 +01:00
parent cc8e78b505
commit be319c446d
75 changed files with 955 additions and 1431 deletions

View File

@@ -44,9 +44,14 @@ Array<AudioDevice> Audio::Devices;
Action Audio::DevicesChanged;
Action Audio::ActiveDeviceChanged;
AudioBackend* AudioBackend::Instance = nullptr;
float MasterVolume = 1.0f;
float Volume = 1.0f;
int32 ActiveDeviceIndex = -1;
namespace
{
float MasterVolume = 1.0f;
float Volume = 1.0f;
int32 ActiveDeviceIndex = -1;
bool MuteOnFocusLoss = true;
}
class AudioService : public EngineService
{
@@ -77,6 +82,11 @@ namespace
}
}
void AudioSettings::Apply()
{
::MuteOnFocusLoss = MuteOnFocusLoss;
}
AudioDevice* Audio::GetActiveDevice()
{
return &Devices[ActiveDeviceIndex];
@@ -161,7 +171,7 @@ void Audio::OnRemoveSource(AudioSource* source)
bool AudioService::Init()
{
const auto settings = AudioSettings::Instance();
const auto settings = AudioSettings::Get();
const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio;
// Pick a backend to use
@@ -225,11 +235,9 @@ void AudioService::Update()
{
PROFILE_CPU();
const auto settings = AudioSettings::Instance();
// Update the master volume
float masterVolume = MasterVolume;
if (settings->MuteOnFocusLoss && !Engine::HasFocus)
if (MuteOnFocusLoss && !Engine::HasFocus)
{
// Mute audio if app has no user focus
masterVolume = 0.0f;

View File

@@ -8,35 +8,38 @@
/// <summary>
/// Audio settings container.
/// </summary>
/// <seealso cref="SettingsBase{AudioSettings}" />
class AudioSettings : public SettingsBase<AudioSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AudioSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(AudioSettings);
public:
/// <summary>
/// If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine.
/// </summary>
API_FIELD(Attributes="EditorOrder(0), DefaultValue(false), EditorDisplay(\"General\")")
bool DisableAudio = false;
/// <summary>
/// The doppler effect factor. Scale for source and listener velocities. Default is 1.
/// </summary>
API_FIELD(Attributes="EditorOrder(100), DefaultValue(1.0f), EditorDisplay(\"General\")")
float DopplerFactor = 1.0f;
/// <summary>
/// True if mute all audio playback when game has no use focus.
/// </summary>
API_FIELD(Attributes="EditorOrder(200), DefaultValue(true), EditorDisplay(\"General\", \"Mute On Focus Loss\")")
bool MuteOnFocusLoss = true;
public:
/// <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 AudioSettings* Get();
// [SettingsBase]
void RestoreDefault() final override
{
DisableAudio = false;
DopplerFactor = 1.0f;
MuteOnFocusLoss = true;
}
void Apply() override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{

View File

@@ -878,7 +878,7 @@ bool AudioBackendOAL::Base_Init()
// Init
ALC::AL_EXT_float32 = ALC::IsExtensionSupported("AL_EXT_float32");
SetDopplerFactor(AudioSettings::Instance()->DopplerFactor);
SetDopplerFactor(AudioSettings::Get()->DopplerFactor);
ALC::RebuildContexts(true);
Audio::SetActiveDeviceIndex(activeDeviceIndex);

View File

@@ -794,7 +794,7 @@ void AudioBackendXAudio2::Base_Update()
}
// Update dirty voices
const float dopplerFactor = AudioSettings::Instance()->DopplerFactor;
const float dopplerFactor = AudioSettings::Get()->DopplerFactor;
X3DAUDIO_DSP_SETTINGS dsp = { 0 };
dsp.DstChannelCount = XAudio2::Channels;
dsp.pMatrixCoefficients = XAudio2::MatrixCoefficients;

View File

@@ -214,6 +214,7 @@ Asset::LoadResult JsonAsset::loadAsset()
if (!instance)
return LoadResult::Failed;
Instance = instance;
InstanceType = typeHandle;
_dtor = type.Class.Dtor;
type.Class.Ctor(instance);
@@ -238,6 +239,7 @@ void JsonAsset::unload(bool isReloading)
if (Instance)
{
InstanceType = ScriptingTypeHandle();
_dtor(Instance);
Allocator::Free(Instance);
Instance = nullptr;

View File

@@ -74,7 +74,6 @@ protected:
/// <summary>
/// Generic type of Json-format asset. It provides the managed representation of this resource data so it can be accessed via C# API.
/// </summary>
/// <seealso cref="JsonAssetBase" />
API_CLASS(NoSpawn) class JsonAsset : public JsonAssetBase
{
DECLARE_ASSET_HEADER(JsonAsset);
@@ -83,6 +82,11 @@ private:
public:
/// <summary>
/// The scripting type of the deserialized unmanaged object instance (e.g. PhysicalMaterial).
/// </summary>
ScriptingTypeHandle InstanceType;
/// <summary>
/// The deserialized unmanaged object instance (e.g. PhysicalMaterial).
/// </summary>

View File

@@ -10,71 +10,73 @@
/// <summary>
/// The game building rendering settings.
/// </summary>
class BuildSettings : public SettingsBase<BuildSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API BuildSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(BuildSettings);
public:
/// <summary>
/// The maximum amount of assets to include into a single assets package. Asset packages will split into several packages if need to.
/// </summary>
int32 MaxAssetsPerPackage = 1024;
API_FIELD(Attributes="EditorOrder(10), DefaultValue(4096), Limit(1, 32, ushort.MaxValue), EditorDisplay(\"General\", \"Max assets per package\")")
int32 MaxAssetsPerPackage = 4096;
/// <summary>
/// The maximum size of the single assets package (in megabytes). Asset packages will split into several packages if need to.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(1024), Limit(1, 16, ushort.MaxValue), EditorDisplay(\"General\", \"Max package size (in MB)\")")
int32 MaxPackageSizeMB = 1024;
/// <summary>
/// The game content cooking keycode. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building.
/// </summary>
API_FIELD(Attributes="EditorOrder(30), DefaultValue(0), Limit(1, 16, ushort.MaxValue), EditorDisplay(\"General\")")
int32 ContentKey = 0;
/// <summary>
/// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett).
/// </summary>
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"General\")")
bool ForDistribution = false;
/// <summary>
/// If checked, the output build files won't be packaged for the destination platform. Useful when debugging build from local PC.
/// </summary>
API_FIELD(Attributes="EditorOrder(50), DefaultValue(false), EditorDisplay(\"General\")")
bool SkipPackaging = false;
/// <summary>
/// The list of additional assets to include into build (into root assets set).
/// </summary>
API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Additional Data\")")
Array<AssetReference<Asset>> AdditionalAssets;
/// <summary>
/// The list of additional folders with assets to include into build (into root assets set). Paths relative to the project directory (or absolute).
/// </summary>
API_FIELD(Attributes="EditorOrder(1010), EditorDisplay(\"Additional Data\")")
Array<String> AdditionalAssetFolders;
/// <summary>
/// Disables shaders compiler optimizations in cooked game. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.
/// </summary>
API_FIELD(Attributes="EditorOrder(2000), DefaultValue(false), EditorDisplay(\"Content\", \"Shaders No Optimize\")")
bool ShadersNoOptimize = false;
/// <summary>
/// Enables shader debug data generation for shaders in cooked game (depends on the target platform rendering backend).
/// </summary>
API_FIELD(Attributes="EditorOrder(2010), DefaultValue(false), EditorDisplay(\"Content\")")
bool ShadersGenerateDebugData = false;
public:
// [SettingsBase]
void RestoreDefault() final override
{
MaxAssetsPerPackage = 1024;
MaxPackageSizeMB = 1024;
ContentKey = 0;
ForDistribution = false;
SkipPackaging = false;
AdditionalAssets.Clear();
AdditionalAssetFolders.Clear();
ShadersNoOptimize = false;
ShadersGenerateDebugData = false;
}
/// <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 BuildSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(MaxAssetsPerPackage);

View File

@@ -2,6 +2,7 @@
#include "GameSettings.h"
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Scripting/ScriptingType.h"
#include "Engine/Physics/PhysicsSettings.h"
#include "Engine/Core/Log.h"
#include "LayersTagsSettings.h"
@@ -17,30 +18,6 @@
#include "Engine/Content/AssetReference.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Engine/Time.h"
String GameSettings::ProductName;
String GameSettings::CompanyName;
String GameSettings::CopyrightNotice;
Guid GameSettings::Icon;
Guid GameSettings::FirstScene;
bool GameSettings::NoSplashScreen = false;
Guid GameSettings::SplashScreen;
Dictionary<String, Guid> GameSettings::CustomSettings;
Array<Settings*> Settings::Containers(32);
#if USE_EDITOR
extern void LoadPlatformSettingsEditor(ISerializable::DeserializeStream& data);
#endif
void TimeSettings::Apply()
{
Time::UpdateFPS = UpdateFPS;
Time::PhysicsFPS = PhysicsFPS;
Time::DrawFPS = DrawFPS;
Time::TimeScale = TimeScale;
}
class GameSettingsService : public EngineService
{
@@ -49,9 +26,6 @@ public:
GameSettingsService()
: EngineService(TEXT("GameSettings"), -70)
{
GameSettings::Icon = Guid::Empty;
GameSettings::FirstScene = Guid::Empty;
GameSettings::SplashScreen = Guid::Empty;
}
bool Init() override
@@ -60,62 +34,141 @@ public:
}
};
IMPLEMENT_SETTINGS_GETTER(BuildSettings, GameCooking);
IMPLEMENT_SETTINGS_GETTER(GraphicsSettings, Graphics);
IMPLEMENT_SETTINGS_GETTER(LayersAndTagsSettings, LayersAndTags);
IMPLEMENT_SETTINGS_GETTER(TimeSettings, Time);
IMPLEMENT_SETTINGS_GETTER(AudioSettings, Audio);
IMPLEMENT_SETTINGS_GETTER(PhysicsSettings, Physics);
IMPLEMENT_SETTINGS_GETTER(InputSettings, Input);
#if !USE_EDITOR
#if PLATFORM_WINDOWS
IMPLEMENT_SETTINGS_GETTER(WindowsPlatformSettings, WindowsPlatform);
#elif PLATFORM_UWP || PLATFORM_XBOX_ONE
IMPLEMENT_SETTINGS_GETTER(UWPPlatformSettings, UWPPlatform);
#elif PLATFORM_LINUX
IMPLEMENT_SETTINGS_GETTER(LinuxPlatformSettings, LinuxPlatform);
#elif PLATFORM_PS4
IMPLEMENT_SETTINGS_GETTER(PS4PlatformSettings, PS4Platform);
#elif PLATFORM_XBOX_SCARLETT
IMPLEMENT_SETTINGS_GETTER(XboxScarlettPlatformSettings, XboxScarlettPlatform);
#elif PLATFORM_ANDROID
IMPLEMENT_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform);
#else
#error Unknown platform
#endif
#endif
GameSettingsService GameSettingsServiceInstance;
AssetReference<JsonAsset> GameSettingsAsset;
GameSettings* GameSettings::Get()
{
if (!GameSettingsAsset)
{
// Load root game settings asset.
// It may be missing in editor during dev but must be ready in the build game.
const auto assetPath = Globals::ProjectContentFolder / TEXT("GameSettings.json");
GameSettingsAsset = Content::LoadAsync<JsonAsset>(assetPath);
if (GameSettingsAsset == nullptr)
{
LOG(Error, "Missing game settings asset.");
return nullptr;
}
if (GameSettingsAsset->WaitForLoaded())
{
return nullptr;
}
if (GameSettingsAsset->InstanceType != GameSettings::TypeInitializer)
{
LOG(Error, "Invalid game settings asset data type.");
return nullptr;
}
}
auto asset = GameSettingsAsset.Get();
if (asset && asset->WaitForLoaded())
asset = nullptr;
return asset ? (GameSettings*)asset->Instance : nullptr;
}
bool GameSettings::Load()
{
// Load main settings asset
auto settings = Get();
if (!settings)
{
return true;
}
// Preload all settings assets
#define PRELOAD_SETTINGS(type) \
{ \
if (settings->type) \
{ \
Content::LoadAsync<JsonAsset>(settings->type); \
} \
else \
{ \
LOG(Warning, "Missing {0} settings", TEXT(#type)); \
} \
}
PRELOAD_SETTINGS(Time);
PRELOAD_SETTINGS(Audio);
PRELOAD_SETTINGS(LayersAndTags);
PRELOAD_SETTINGS(Physics);
PRELOAD_SETTINGS(Input);
PRELOAD_SETTINGS(Graphics);
PRELOAD_SETTINGS(Navigation);
PRELOAD_SETTINGS(GameCooking);
#undef PRELOAD_SETTINGS
// Apply the game settings to the engine
settings->Apply();
return false;
}
void GameSettings::Apply()
{
// TODO: impl this
#define APPLY_SETTINGS(type) \
{ \
type* obj = type::Get(); \
if (obj) \
{ \
obj->Apply(); \
} \
else \
{ \
LOG(Warning, "Missing {0} settings", TEXT(#type)); \
} \
}
APPLY_SETTINGS(TimeSettings);
APPLY_SETTINGS(AudioSettings);
APPLY_SETTINGS(LayersAndTagsSettings);
APPLY_SETTINGS(PhysicsSettings);
APPLY_SETTINGS(InputSettings);
APPLY_SETTINGS(GraphicsSettings);
APPLY_SETTINGS(NavigationSettings);
APPLY_SETTINGS(BuildSettings);
APPLY_SETTINGS(PlatformSettings);
#undef APPLY_SETTINGS
}
void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
// Load properties
ProductName = JsonTools::GetString(stream, "ProductName");
CompanyName = JsonTools::GetString(stream, "CompanyName");
CopyrightNotice = JsonTools::GetString(stream, "CopyrightNotice");
Icon = JsonTools::GetGuid(stream, "Icon");
FirstScene = JsonTools::GetGuid(stream, "FirstScene");
NoSplashScreen = JsonTools::GetBool(stream, "NoSplashScreen", NoSplashScreen);
SplashScreen = JsonTools::GetGuid(stream, "SplashScreen");
CustomSettings.Clear();
#if USE_EDITOR
#define END_POINT(msg) LOG(Warning, msg " Using default values."); return false
#else
#define END_POINT(msg) LOG(Fatal, msg); return true
#endif
#define LOAD_SETTINGS(nodeName, settingsType) \
{ \
Guid id = JsonTools::GetGuid(data, nodeName); \
if (id.IsValid()) \
{ \
AssetReference<JsonAsset> subAsset = Content::LoadAsync<JsonAsset>(id); \
if (subAsset && !subAsset->WaitForLoaded()) \
{ \
settingsType::Instance()->Deserialize(*subAsset->Data, nullptr); \
settingsType::Instance()->Apply(); \
} \
else \
{ LOG(Warning, "Cannot load " nodeName " settings"); } \
} \
else \
{ LOG(Warning, "Missing " nodeName " settings"); } \
}
// Load root game settings asset.
// It may be missing in editor during dev but must be ready in the build game.
const auto assetPath = Globals::ProjectContentFolder / TEXT("GameSettings.json");
AssetReference<JsonAsset> asset = Content::LoadAsync<JsonAsset>(assetPath);
if (asset == nullptr)
{
END_POINT("Missing game settings asset.");
}
if (asset->WaitForLoaded()
|| asset->DataTypeName != TEXT("FlaxEditor.Content.Settings.GameSettings")
|| asset->Data == nullptr)
{
END_POINT("Cannot load game settings asset.");
}
auto& data = *asset->Data;
// Load settings
ProductName = JsonTools::GetString(data, "ProductName");
CompanyName = JsonTools::GetString(data, "CompanyName");
CopyrightNotice = JsonTools::GetString(data, "CopyrightNotice");
Icon = JsonTools::GetGuid(data, "Icon");
FirstScene = JsonTools::GetGuid(data, "FirstScene");
NoSplashScreen = JsonTools::GetBool(data, "NoSplashScreen", NoSplashScreen);
SplashScreen = JsonTools::GetGuid(data, "SplashScreen");
const auto customSettings = data.FindMember("CustomSettings");
if (customSettings != data.MemberEnd())
const auto customSettings = stream.FindMember("CustomSettings");
if (customSettings != stream.MemberEnd())
{
auto& items = customSettings->value;
for (auto it = items.MemberBegin(); it != items.MemberEnd(); ++it)
@@ -129,39 +182,21 @@ bool GameSettings::Load()
}
}
// Load child settings
LOAD_SETTINGS("Time", TimeSettings);
LOAD_SETTINGS("Physics", PhysicsSettings);
LOAD_SETTINGS("LayersAndTags", LayersAndTagsSettings);
LOAD_SETTINGS("Graphics", GraphicsSettings);
LOAD_SETTINGS("GameCooking", BuildSettings);
LOAD_SETTINGS("Input", InputSettings);
LOAD_SETTINGS("Audio", AudioSettings);
LOAD_SETTINGS("Navigation", NavigationSettings);
// Settings containers
DESERIALIZE(Time);
DESERIALIZE(Audio);
DESERIALIZE(LayersAndTags);
DESERIALIZE(Physics);
DESERIALIZE(Input);
DESERIALIZE(Graphics);
DESERIALIZE(Navigation);
DESERIALIZE(GameCooking);
// Load platform settings
#if PLATFORM_WINDOWS
LOAD_SETTINGS("WindowsPlatform", WindowsPlatformSettings);
#endif
#if PLATFORM_UWP
LOAD_SETTINGS("UWPPlatform", UWPPlatformSettings);
#endif
#if PLATFORM_LINUX
LOAD_SETTINGS("LinuxPlatform", LinuxPlatformSettings);
#endif
#if PLATFORM_PS4
LOAD_SETTINGS("PS4Platform", PS4PlatformSettings);
#endif
#if PLATFORM_XBOX_SCARLETT
LOAD_SETTINGS("XboxScarlettPlatform", XboxScarlettPlatformSettings);
#endif
#if PLATFORM_ANDROID
LOAD_SETTINGS("AndroidPlatform", AndroidPlatformSettings);
#endif
#if USE_EDITOR
LoadPlatformSettingsEditor(data);
#endif
return false;
#undef END_POINT
// Per-platform settings containers
DESERIALIZE(WindowsPlatform);
DESERIALIZE(UWPPlatform);
DESERIALIZE(LinuxPlatform);
DESERIALIZE(PS4Platform);
DESERIALIZE(XboxScarlettPlatform);
DESERIALIZE(AndroidPlatform);
}

View File

@@ -2,60 +2,96 @@
#pragma once
#include "Settings.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Collections/Dictionary.h"
/// <summary>
/// Main engine configuration service. Loads and applies game configuration.
/// The main game engine configuration service. Loads and applies game configuration.
/// </summary>
class GameSettings
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API GameSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(GameSettings);
public:
/// <summary>
/// The product full name.
/// </summary>
static String ProductName;
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"General\")")
String ProductName;
/// <summary>
/// The company full name.
/// </summary>
static String CompanyName;
API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"General\")")
String CompanyName;
/// <summary>
/// The copyright note used for content signing (eg. source code header).
/// </summary>
static String CopyrightNotice;
API_FIELD(Attributes="EditorOrder(15), EditorDisplay(\"General\")")
String CopyrightNotice;
/// <summary>
/// The default application icon.
/// </summary>
static Guid Icon;
Guid Icon = Guid::Empty;
/// <summary>
/// Reference to the first scene to load on a game startup.
/// </summary>
static Guid FirstScene;
Guid FirstScene = Guid::Empty;
/// <summary>
/// True if skip showing splash screen image on the game startup.
/// </summary>
static bool NoSplashScreen;
bool NoSplashScreen = false;
/// <summary>
/// Reference to the splash screen image to show on a game startup.
/// </summary>
static Guid SplashScreen;
Guid SplashScreen = Guid::Empty;
/// <summary>
/// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).
/// </summary>
static Dictionary<String, Guid> CustomSettings;
Dictionary<String, Guid> CustomSettings;
public:
// Settings containers
Guid Time;
Guid Audio;
Guid LayersAndTags;
Guid Physics;
Guid Input;
Guid Graphics;
Guid Navigation;
Guid GameCooking;
// Per-platform settings containers
Guid WindowsPlatform;
Guid UWPPlatform;
Guid LinuxPlatform;
Guid PS4Platform;
Guid XboxScarlettPlatform;
Guid AndroidPlatform;
public:
/// <summary>
/// Gets the instance of the game settings asset (null if missing). Object returned by this method is always loaded with valid data to use.
/// </summary>
static GameSettings* Get();
/// <summary>
/// Loads the game settings (including other settings such as Physics, Input, etc.).
/// </summary>
/// <returns>True if failed, otherwise false.</returns>
static bool Load();
// [SettingsBase]
void Apply() override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override;
};

View File

@@ -9,67 +9,68 @@
/// <summary>
/// Graphics rendering settings.
/// </summary>
class GraphicsSettings : public SettingsBase<GraphicsSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API GraphicsSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(GraphicsSettings);
public:
/// <summary>
/// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(false), EditorDisplay(\"General\", \"Use V-Sync\")")
bool UseVSync = false;
/// <summary>
/// Anti Aliasing quality setting.
/// </summary>
API_FIELD(Attributes="EditorOrder(1000), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\", \"AA Quality\")")
Quality AAQuality = Quality::Medium;
/// <summary>
/// Screen Space Reflections quality setting.
/// </summary>
API_FIELD(Attributes="EditorOrder(1100), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\", \"SSR Quality\")")
Quality SSRQuality = Quality::Medium;
/// <summary>
/// Screen Space Ambient Occlusion quality setting.
/// </summary>
API_FIELD(Attributes="EditorOrder(1200), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\", \"SSAO Quality\")")
Quality SSAOQuality = Quality::Medium;
/// <summary>
/// Volumetric Fog quality setting.
/// </summary>
API_FIELD(Attributes="EditorOrder(1250), DefaultValue(Quality.High), EditorDisplay(\"Quality\")")
Quality VolumetricFogQuality = Quality::High;
/// <summary>
/// The shadows quality.
/// </summary>
API_FIELD(Attributes="EditorOrder(1300), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\")")
Quality ShadowsQuality = Quality::Medium;
/// <summary>
/// The shadow maps quality (textures resolution).
/// </summary>
API_FIELD(Attributes="EditorOrder(1310), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\")")
Quality ShadowMapsQuality = Quality::Medium;
/// <summary>
/// Enables cascades splits blending for directional light shadows.
/// </summary>
API_FIELD(Attributes="EditorOrder(1320), DefaultValue(false), EditorDisplay(\"Quality\", \"Allow CSM Blending\")")
bool AllowCSMBlending = false;
public:
/// <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 GraphicsSettings* Get();
// [SettingsBase]
void Apply() override;
void RestoreDefault() final override
{
UseVSync = false;
AAQuality = Quality::Medium;
SSRQuality = Quality::Medium;
SSAOQuality = Quality::Medium;
VolumetricFogQuality = Quality::High;
ShadowsQuality = Quality::Medium;
ShadowMapsQuality = Quality::Medium;
AllowCSMBlending = false;
}
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(UseVSync);

View File

@@ -7,8 +7,9 @@
/// <summary>
/// Layers and objects tags settings.
/// </summary>
class LayersAndTagsSettings : public SettingsBase<LayersAndTagsSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API LayersAndTagsSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(LayersAndTagsSettings);
public:
/// <summary>
@@ -24,43 +25,11 @@ public:
public:
/// <summary>
/// Gets or adds the tag (returns the tag index).
/// 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>
/// <param name="tag">The tag.</param>
/// <returns>The tag index.</returns>
int32 GetOrAddTag(const String& tag)
{
int32 index = Tags.Find(tag);
if (index == INVALID_INDEX)
{
index = Tags.Count();
Tags.Add(tag);
}
return index;
}
/// <summary>
/// Gets the amount of non empty layer names (from the beginning, trims the last ones).
/// </summary>
/// <returns>The layers count.</returns>
int32 GetNonEmptyLayerNamesCount() const
{
int32 result = 31;
while (result >= 0 && Layers[result].IsEmpty())
result--;
return result + 1;
}
public:
static LayersAndTagsSettings* Get();
// [SettingsBase]
void RestoreDefault() override
{
Tags.Clear();
for (int32 i = 0; i < 32; i++)
Layers[i].Clear();
}
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
const auto tags = stream.FindMember("Tags");

View File

@@ -2,81 +2,46 @@
#pragma once
#include "Engine/Core/Singleton.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Serialization/ISerializable.h"
/// <summary>
/// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to engine/game.
/// </summary>
class FLAXENGINE_API Settings
API_CLASS(Abstract) class FLAXENGINE_API SettingsBase : public ISerializable
{
DECLARE_SCRIPTING_TYPE_MINIMAL(SettingsBase);
public:
/// <summary>
/// The settings containers.
/// </summary>
static Array<Settings*> Containers;
/// <summary>
/// Restores the default settings for all the registered containers.
/// </summary>
static void RestoreDefaultAll()
{
for (int32 i = 0; i < Containers.Count(); i++)
Containers[i]->RestoreDefault();
}
private:
// Disable copy/move
Settings(const Settings&) = delete;
Settings& operator=(const Settings&) = delete;
protected:
Settings()
{
Containers.Add(this);
}
public:
virtual ~Settings() = default;
public:
typedef ISerializable::DeserializeStream DeserializeStream;
/// <summary>
/// Applies the settings to the target services.
/// Applies the settings to the target system.
/// </summary>
virtual void Apply()
{
}
/// <summary>
/// Restores the default settings.
/// </summary>
virtual void RestoreDefault() = 0;
public:
/// <summary>
/// Deserializes the settings container.
/// </summary>
/// <param name="stream">The input data stream.</param>
/// <param name="modifier">The deserialization modifier object. Always valid.</param>
virtual void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) = 0;
};
/// <summary>
/// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to engine/game.
/// </summary>
template<class T>
class SettingsBase : public Settings, public Singleton<T>
{
protected:
SettingsBase()
// [ISerializable]
void Serialize(SerializeStream& stream, const void* otherObj) override
{
// Not supported (Editor C# edits settings data)
}
};
// Helper utility define for settings getter implementation code
#define IMPLEMENT_SETTINGS_GETTER(type, field) \
type* type::Get() \
{ \
static type DefaultInstance; \
type* result = &DefaultInstance; \
const auto gameSettings = GameSettings::Get(); \
if (gameSettings) \
{ \
const auto asset = Content::Load<JsonAsset>(gameSettings->field); \
if (asset && asset->Instance && asset->InstanceType == type::TypeInitializer) \
{ \
result = static_cast<type*>(asset->Instance); \
} \
} \
return result; \
}

View File

@@ -3,60 +3,53 @@
#pragma once
#include "Engine/Core/Config/Settings.h"
#include "Engine/Serialization/Serialization.h"
/// <summary>
/// Time and game simulation settings container.
/// </summary>
class TimeSettings : public SettingsBase<TimeSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API TimeSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(TimeSettings);
public:
/// <summary>
/// The target amount of the game logic updates per second (script updates frequency).
/// </summary>
API_FIELD(Attributes="EditorOrder(1), DefaultValue(30.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Update FPS\")")
float UpdateFPS = 30.0f;
/// <summary>
/// The target amount of the physics simulation updates per second (also fixed updates frequency).
/// </summary>
API_FIELD(Attributes="EditorOrder(2), DefaultValue(60.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Physics FPS\")")
float PhysicsFPS = 60.0f;
/// <summary>
/// The target amount of the frames rendered per second (actual game FPS).
/// </summary>
API_FIELD(Attributes="EditorOrder(3), DefaultValue(60.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Draw FPS\")")
float DrawFPS = 60.0f;
/// <summary>
/// The game time scale factor. Default is 1.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(1.0f), Limit(0, 1000.0f, 0.1f), EditorDisplay(\"General\")")
float TimeScale = 1.0f;
/// <summary>
/// The maximum allowed delta time (in seconds) for the game logic update step.
/// </summary>
float MaxUpdateDeltaTime = (1.0f / 10.0f);
API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.1f), Limit(0.1f, 1000.0f, 0.01f), EditorDisplay(\"General\")")
float MaxUpdateDeltaTime = 0.1f;
public:
/// <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 TimeSettings* Get();
// [SettingsBase]
void Apply() override;
void RestoreDefault() override
{
UpdateFPS = 30.0f;
PhysicsFPS = 60.0f;
DrawFPS = 60.0f;
TimeScale = 1.0f;
MaxUpdateDeltaTime = 1.0f / 10.0f;
}
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(UpdateFPS);
DESERIALIZE(PhysicsFPS);
DESERIALIZE(DrawFPS);
DESERIALIZE(TimeScale);
DESERIALIZE(MaxUpdateDeltaTime);
}
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override;
};

View File

@@ -5,8 +5,8 @@
void Math::SinCos(float angle, float& sine, float& cosine)
{
sine = sin(angle);
cosine = cos(angle);
sine = Math::Sin(angle);
cosine = Math::Cos(angle);
}
uint32 Math::FloorLog2(uint32 value)

View File

@@ -119,7 +119,10 @@ bool GameBase::Init()
}
// Preload first scene asset data
GameBaseImpl::FirstScene = GameSettings::FirstScene;
const auto gameSettings = GameSettings::Get();
if (!gameSettings)
return true;
GameBaseImpl::FirstScene = gameSettings->FirstScene;
return false;
}
@@ -261,8 +264,8 @@ void GameBaseImpl::OnSplashScreenEnd()
// Load the first scene
LOG(Info, "Loading the first scene");
const auto sceneId = FirstScene ? FirstScene.GetID() : Guid::Empty;
FirstScene.Unlink();
const auto sceneId = GameSettings::FirstScene;
if (Level::LoadSceneAsync(sceneId))
{
LOG(Fatal, "Cannot load the first scene.");

View File

@@ -51,6 +51,9 @@
namespace EngineImpl
{
bool IsReady = false;
#if !USE_EDITOR
bool RunInBackground = false;
#endif
String CommandLine = nullptr;
int32 Fps = 0, FpsAccumulatedFrames = 0;
double FpsAccumulated = 0.0;
@@ -133,6 +136,9 @@ int32 Engine::Main(const Char* cmdLine)
Platform::BeforeRun();
EngineImpl::InitMainWindow();
Application::BeforeRun();
#if !USE_EDITOR && (PLATFORM_WINDOWS || PLATFORM_LINUX)
EngineImpl::RunInBackground = PlatformSettings::Get()->RunInBackground;
#endif
Log::Logger::WriteFloor();
LOG_FLUSH();
Time::OnBeforeRun();
@@ -279,11 +285,7 @@ void Engine::OnUpdate()
bool isGameRunning = true;
if (mainWindow && !mainWindow->IsFocused())
{
#if PLATFORM_WINDOWS || PLATFORM_LINUX
isGameRunning = PlatformSettings::Instance()->RunInBackground;
#else
isGameRunning = false;
#endif
isGameRunning = EngineImpl::RunInBackground;
}
Time::SetGamePaused(!isGameRunning);
#endif
@@ -393,8 +395,11 @@ const String& Engine::GetCommandLine()
JsonAsset* Engine::GetCustomSettings(const StringView& key)
{
const auto settings = GameSettings::Get();
if (!settings)
return nullptr;
Guid assetId = Guid::Empty;
GameSettings::CustomSettings.TryGet(key, assetId);
settings->CustomSettings.TryGet(key, assetId);
return Content::LoadAsync<JsonAsset>(assetId);
}

View File

@@ -16,7 +16,7 @@ void LinuxGame::InitMainWindowSettings(CreateWindowSettings& settings)
{
// TODO: restore window size and fullscreen mode from the cached local settings saved after previous session
const auto platformSettings = LinuxPlatformSettings::Instance();
const auto platformSettings = LinuxPlatformSettings::Get();
auto windowMode = platformSettings->WindowMode;
// Use command line switches
@@ -54,7 +54,7 @@ void LinuxGame::InitMainWindowSettings(CreateWindowSettings& settings)
bool LinuxGame::Init()
{
const auto platformSettings = LinuxPlatformSettings::Instance();
const auto platformSettings = LinuxPlatformSettings::Get();
// Create mutex if need to
if (platformSettings->ForceSingleInstance)

View File

@@ -3,17 +3,19 @@
#include "Time.h"
#include "EngineService.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Core/Config/TimeSettings.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Physics/PhysicsSettings.h"
#include "Engine/Core/Config/TimeSettings.h"
#include "Engine/Serialization/Serialization.h"
namespace
{
bool FixedDeltaTimeEnable;
float FixedDeltaTimeValue;
float MaxUpdateDeltaTime = 0.1f;
}
bool Time::_gamePaused = false;
float Time::_physicsMaxDeltaTime = 0.1f;
DateTime Time::StartupTime;
float Time::UpdateFPS = 30.0f;
float Time::PhysicsFPS = 60.0f;
@@ -43,6 +45,24 @@ public:
TimeService TimeServiceInstance;
void TimeSettings::Apply()
{
Time::UpdateFPS = UpdateFPS;
Time::PhysicsFPS = PhysicsFPS;
Time::DrawFPS = DrawFPS;
Time::TimeScale = TimeScale;
::MaxUpdateDeltaTime = MaxUpdateDeltaTime;
}
void TimeSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
DESERIALIZE(UpdateFPS);
DESERIALIZE(PhysicsFPS);
DESERIALIZE(DrawFPS);
DESERIALIZE(TimeScale);
DESERIALIZE(MaxUpdateDeltaTime);
}
void Time::TickData::OnBeforeRun(float targetFps, double currentTime)
{
Time = UnscaledTime = TimeSpan::Zero();
@@ -219,7 +239,7 @@ void Time::OnBeforeRun()
bool Time::OnBeginUpdate()
{
if (Update.OnTickBegin(UpdateFPS, TimeSettings::Instance()->MaxUpdateDeltaTime))
if (Update.OnTickBegin(UpdateFPS, MaxUpdateDeltaTime))
{
Current = &Update;
return true;
@@ -229,7 +249,7 @@ bool Time::OnBeginUpdate()
bool Time::OnBeginPhysics()
{
if (Physics.OnTickBegin(PhysicsFPS, PhysicsSettings::Instance()->MaxDeltaTime))
if (Physics.OnTickBegin(PhysicsFPS, _physicsMaxDeltaTime))
{
Current = &Physics;
return true;

View File

@@ -15,6 +15,7 @@ API_CLASS(Static) class FLAXENGINE_API Time
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Time);
friend class Engine;
friend class TimeService;
friend class PhysicsSettings;
public:
/// <summary>
@@ -100,6 +101,7 @@ public:
private:
static bool _gamePaused;
static float _physicsMaxDeltaTime;
public:

View File

@@ -11,7 +11,7 @@ void WindowsGame::InitMainWindowSettings(CreateWindowSettings& settings)
{
// TODO: restore window size and fullscreen mode from the cached local settings saved after previous session
const auto platformSettings = WindowsPlatformSettings::Instance();
const auto platformSettings = WindowsPlatformSettings::Get();
auto windowMode = platformSettings->WindowMode;
// Use command line switches
@@ -45,7 +45,7 @@ void WindowsGame::InitMainWindowSettings(CreateWindowSettings& settings)
bool WindowsGame::Init()
{
const auto platformSettings = WindowsPlatformSettings::Instance();
const auto platformSettings = WindowsPlatformSettings::Get();
// Create mutex if need to
if (platformSettings->ForceSingleInstance)

View File

@@ -91,7 +91,7 @@ GPUDevice* GPUDeviceDX11::Create()
else if (CommandLine::Options.D3D11)
maxAllowedFeatureLevel = D3D_FEATURE_LEVEL_11_0;
#if !USE_EDITOR && PLATFORM_WINDOWS
auto winSettings = WindowsPlatformSettings::Instance();
auto winSettings = WindowsPlatformSettings::Get();
if (!winSettings->SupportDX11 && !winSettings->SupportDX10)
{
// Skip if there is no support

View File

@@ -45,7 +45,7 @@ GPUDevice* GPUDeviceDX12::Create()
selectedAdapter.Description.VendorId = GPU_VENDOR_ID_AMD;
#else
#if !USE_EDITOR && PLATFORM_WINDOWS
auto winSettings = WindowsPlatformSettings::Instance();
auto winSettings = WindowsPlatformSettings::Get();
if (!winSettings->SupportDX12)
{
// Skip if there is no support

View File

@@ -1076,7 +1076,7 @@ GPUDeviceVulkan::GPUDeviceVulkan(ShaderProfile shaderProfile, GPUAdapterVulkan*
GPUDevice* GPUDeviceVulkan::Create()
{
#if !USE_EDITOR && (PLATFORM_WINDOWS || PLATFORM_LINUX)
auto settings = PlatformSettings::Instance();
auto settings = PlatformSettings::Get();
if (!settings->SupportVulkan)
{
// Skip if there is no support

View File

@@ -98,6 +98,12 @@ Delegate<StringView> Input::ActionTriggered;
Array<ActionConfig> Input::ActionMappings;
Array<AxisConfig> Input::AxisMappings;
void InputSettings::Apply()
{
Input::ActionMappings = ActionMappings;
Input::AxisMappings = AxisMappings;
}
int32 Input::GetGamepadsCount()
{
return Gamepads.Count();

View File

@@ -21,7 +21,6 @@ class InputDevice;
API_CLASS(Static) class FLAXENGINE_API Input
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Input);
public:
/// <summary>
/// Gets the mouse (null if platform does not support mouse or it is not connected).

View File

@@ -5,14 +5,13 @@
#include "Engine/Core/Config/Settings.h"
#include "Engine/Serialization/JsonTools.h"
#include "VirtualInput.h"
#include "Input.h"
/// <summary>
/// Input settings container.
/// </summary>
/// <seealso cref="SettingsBase{InputSettings}" />
class InputSettings : public SettingsBase<InputSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API InputSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(InputSettings);
public:
/// <summary>
@@ -27,19 +26,14 @@ public:
public:
// [SettingsBase]
void Apply() override
{
Input::ActionMappings = ActionMappings;
Input::AxisMappings = AxisMappings;
}
/// <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 InputSettings* Get();
// [SettingsBase]
void Apply() override;
void RestoreDefault() override
{
ActionMappings.Resize(0);
AxisMappings.Resize(0);
}
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
const auto actionMappings = stream.FindMember("ActionMappings");

View File

@@ -8,7 +8,6 @@
#include "Prefabs/Prefab.h"
#include "Prefabs/PrefabManager.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Config/LayersTagsSettings.h"
#include "Engine/Scripting/Script.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Threading/Threading.h"
@@ -386,21 +385,19 @@ Actor* Actor::GetChildByPrefabObjectId(const Guid& prefabObjectId) const
bool Actor::HasTag(const StringView& tag) const
{
return HasTag() && tag == LayersAndTagsSettings::Instance()->Tags[_tag];
return HasTag() && tag == Level::Tags[_tag];
}
const String& Actor::GetLayerName() const
{
const auto settings = LayersAndTagsSettings::Instance();
return settings->Layers[_layer];
return Level::Layers[_layer];
}
const String& Actor::GetTag() const
{
if (HasTag())
{
const auto settings = LayersAndTagsSettings::Instance();
return settings->Tags[_tag];
return Level::Tags[_tag];
}
return String::Empty;
}
@@ -420,13 +417,13 @@ void Actor::SetTagIndex(int32 tagIndex)
if (tagIndex == ACTOR_TAG_INVALID)
{
}
else if (LayersAndTagsSettings::Instance()->Tags.IsEmpty())
else if (Level::Tags.IsEmpty())
{
tagIndex = ACTOR_TAG_INVALID;
}
else
{
tagIndex = tagIndex < 0 ? ACTOR_TAG_INVALID : Math::Min(tagIndex, LayersAndTagsSettings::Instance()->Tags.Count() - 1);
tagIndex = tagIndex < 0 ? ACTOR_TAG_INVALID : Math::Min(tagIndex, Level::Tags.Count() - 1);
}
if (tagIndex == _tag)
return;
@@ -444,7 +441,7 @@ void Actor::SetTag(const StringView& tagName)
}
else
{
tagIndex = LayersAndTagsSettings::Instance()->Tags.Find(tagName);
tagIndex = Level::Tags.Find(tagName);
if (tagIndex == -1)
{
LOG(Error, "Cannot change actor tag. Given value is invalid.");
@@ -971,7 +968,7 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
if (tag->value.IsString() && tag->value.GetStringLength())
{
const String tagName = tag->value.GetText();
_tag = LayersAndTagsSettings::Instance()->GetOrAddTag(tagName);
_tag = Level::GetOrAddTag(tagName);
}
}

View File

@@ -9,6 +9,7 @@
#include "Engine/Core/Cache.h"
#include "Engine/Core/Collections/CollectionPoolCache.h"
#include "Engine/Core/ObjectsRemovalService.h"
#include "Engine/Core/Config/LayersTagsSettings.h"
#include "Engine/Debug/Exceptions/ArgumentException.h"
#include "Engine/Debug/Exceptions/ArgumentNullException.h"
#include "Engine/Debug/Exceptions/InvalidOperationException.h"
@@ -97,6 +98,7 @@ public:
{
}
bool Init() override;
void Update() override;
void LateUpdate() override;
void FixedUpdate() override;
@@ -124,6 +126,8 @@ Delegate<Scene*, const Guid&> Level::SceneUnloaded;
Action Level::ScriptsReloadStart;
Action Level::ScriptsReload;
Action Level::ScriptsReloadEnd;
Array<String> Level::Tags;
String Level::Layers[32];
bool LevelImpl::spawnActor(Actor* actor, Actor* parent)
{
@@ -158,6 +162,15 @@ bool LevelImpl::deleteActor(Actor* actor)
return false;
}
bool LevelService::Init()
{
auto& settings = *LayersAndTagsSettings::Get();
Level::Tags = settings.Tags;
for (int32 i = 0; i < ARRAY_COUNT(Level::Layers); i++)
Level::Layers[i] = settings.Layers[i];
return false;
}
void LevelService::Update()
{
PROFILE_CPU();
@@ -642,6 +655,25 @@ void LevelImpl::CallSceneEvent(SceneEventType eventType, Scene* scene, Guid scen
}
}
int32 Level::GetOrAddTag(const StringView& tag)
{
int32 index = Tags.Find(tag);
if (index == INVALID_INDEX)
{
index = Tags.Count();
Tags.AddOne() = tag;
}
return index;
}
int32 Level::GetNonEmptyLayerNamesCount()
{
int32 result = 31;
while (result >= 0 && Layers[result].IsEmpty())
result--;
return result + 1;
}
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
{
PROFILE_CPU();

View File

@@ -420,6 +420,31 @@ public:
/// <param name="output">Output array with only parents</param>
static void ConstructParentActorsTreeList(const Array<Actor*>& input, Array<Actor*>& output);
public:
/// <summary>
/// The tags names.
/// </summary>
static Array<String> Tags;
/// <summary>
/// The layers names.
/// </summary>
static String Layers[32];
/// <summary>
/// Gets or adds the tag (returns the tag index).
/// </summary>
/// <param name="tag">The tag.</param>
/// <returns>The tag index.</returns>
static int32 GetOrAddTag(const StringView& tag);
/// <summary>
/// Gets the amount of non empty layer names (from the beginning, trims the last ones).
/// </summary>
/// <returns>The layers count.</returns>
static int32 GetNonEmptyLayerNamesCount();
private:
// Actor API

View File

@@ -461,13 +461,13 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou
float GetTileSize()
{
auto& settings = *NavigationSettings::Instance();
auto& settings = *NavigationSettings::Get();
return settings.CellSize * settings.TileSize;
}
void InitConfig(rcConfig& config)
{
auto& settings = *NavigationSettings::Instance();
auto& settings = *NavigationSettings::Get();
config.cs = settings.CellSize;
config.ch = settings.CellHeight;

View File

@@ -1,8 +1,12 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "Navigation.h"
#include "NavigationSettings.h"
#include "NavMeshRuntime.h"
#include "NavMeshBuilder.h"
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/JsonAsset.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Engine/EngineService.h"
@@ -55,6 +59,8 @@ void* rcAllocDefault(size_t size, rcAllocHint)
return Allocator::Allocate(size);
}
IMPLEMENT_SETTINGS_GETTER(NavigationSettings, Navigation);
bool NavigationService::Init()
{
// Link memory allocation calls to use engine default allocator

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
namespace FlaxEditor.Content.Settings
{
partial class NavigationSettings
{
}
}

View File

@@ -9,7 +9,7 @@
/// <summary>
/// The navigation system settings container.
/// </summary>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API NavigationSettings : public SettingsBase<NavigationSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API NavigationSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(NavigationSettings);
public:
@@ -94,24 +94,12 @@ public:
public:
// [SettingsBase]
void RestoreDefault() final override
{
CellHeight = 10.0f;
CellSize = 30.0f;
TileSize = 64;
MinRegionArea = 0;
MergeRegionArea = 20;
MaxEdgeLen = 1200.0f;
MaxEdgeError = 1.3f;
DetailSamplingDist = 600.0f;
MaxDetailSamplingError = 1.0f;
WalkableRadius = 34.0f;
WalkableHeight = 144.0f;
WalkableMaxClimb = 35.0f;
WalkableMaxSlopeAngle = 60.0f;
}
/// <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 NavigationSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(CellHeight);

View File

@@ -232,7 +232,7 @@ void Collider::UpdateLayerBits()
filterData.word0 = GetLayerMask();
// Own layer mask
filterData.word1 = PhysicsSettings::Instance()->LayerMasks[GetLayer()];
filterData.word1 = Physics::LayerMasks[GetLayer()];
_shape->setSimulationFilterData(filterData);
_shape->setQueryFilterData(filterData);

View File

@@ -1,52 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "PhysicalMaterial.h"
#include "PhysicsSettings.h"
#include "Physics.h"
#include <ThirdParty/PhysX/PxPhysics.h>
#include <ThirdParty/PhysX/PxMaterial.h>
PhysicalMaterial::PhysicalMaterial()
: _material(nullptr)
{
}
PhysicalMaterial::~PhysicalMaterial()
{
if (_material)
{
Physics::RemoveMaterial(_material);
}
}
PxMaterial* PhysicalMaterial::GetPhysXMaterial()
{
if (_material == nullptr && CPhysX)
{
_material = CPhysX->createMaterial(Friction, Friction, Restitution);
_material->userData = this;
const PhysicsCombineMode useFrictionCombineMode = (OverrideFrictionCombineMode ? FrictionCombineMode : PhysicsSettings::Instance()->FrictionCombineMode);
_material->setFrictionCombineMode(static_cast<PxCombineMode::Enum>(useFrictionCombineMode));
const PhysicsCombineMode useRestitutionCombineMode = (OverrideRestitutionCombineMode ? RestitutionCombineMode : PhysicsSettings::Instance()->RestitutionCombineMode);
_material->setRestitutionCombineMode(static_cast<PxCombineMode::Enum>(useRestitutionCombineMode));
}
return _material;
}
void PhysicalMaterial::UpdatePhysXMaterial()
{
if (_material != nullptr)
{
_material->setStaticFriction(Friction);
_material->setDynamicFriction(Friction);
const PhysicsCombineMode useFrictionCombineMode = (OverrideFrictionCombineMode ? FrictionCombineMode : PhysicsSettings::Instance()->FrictionCombineMode);
_material->setFrictionCombineMode(static_cast<PxCombineMode::Enum>(useFrictionCombineMode));
_material->setRestitution(Restitution);
const PhysicsCombineMode useRestitutionCombineMode = (OverrideRestitutionCombineMode ? RestitutionCombineMode : PhysicsSettings::Instance()->RestitutionCombineMode);
_material->setRestitutionCombineMode(static_cast<PxCombineMode::Enum>(useRestitutionCombineMode));
}
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "Physics.h"
#include "PhysicalMaterial.h"
#include "Engine/Core/Log.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Platform/CPUInfo.h"
@@ -13,6 +14,8 @@
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Core/Memory/Memory.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Engine/Time.h"
#include <ThirdParty/PhysX/PxPhysicsAPI.h>
#include <ThirdParty/PhysX/PxActor.h>
#if WITH_PVD
@@ -110,34 +113,43 @@ struct ActionData
};
PxPhysics* CPhysX = nullptr;
PhysXAllocator PhysXAllocatorCallback;
PhysXError PhysXErrorCallback;
PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader;
#if WITH_PVD
PxPvd* CPVD = nullptr;
#endif
PxTolerancesScale ToleranceScale;
SimulationEventCallback EventsCallback;
void* ScratchMemory = nullptr;
FixedStepper* Stepper = nullptr;
CriticalSection FlushLocker;
Array<PxActor*> NewActors;
Array<ActionData> Actions;
Array<PxActor*> DeadActors;
Array<PxMaterial*> DeadMaterials;
Array<PxBase*> _deadObjects;
Array<PhysicsColliderActor*> DeadColliders;
Array<Joint*> DeadJoints;
bool _isDuringSimulation = false;
PxFoundation* _foundation = nullptr;
namespace
{
PhysXAllocator PhysXAllocatorCallback;
PhysXError PhysXErrorCallback;
PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader;
PxTolerancesScale ToleranceScale;
SimulationEventCallback EventsCallback;
void* ScratchMemory = nullptr;
FixedStepper* Stepper = nullptr;
CriticalSection FlushLocker;
Array<PxActor*> NewActors;
Array<ActionData> Actions;
Array<PxActor*> DeadActors;
Array<PxMaterial*> DeadMaterials;
Array<PxBase*> _deadObjects;
Array<PhysicsColliderActor*> DeadColliders;
Array<Joint*> DeadJoints;
bool _queriesHitTriggers = true;
bool _isDuringSimulation = false;
PhysicsCombineMode _frictionCombineMode = PhysicsCombineMode::Average;
PhysicsCombineMode _restitutionCombineMode = PhysicsCombineMode::Average;
PxFoundation* _foundation = nullptr;
#if COMPILE_WITH_PHYSICS_COOKING
PxCooking* Cooking = nullptr;
PxCooking* Cooking = nullptr;
#endif
PxScene* PhysicsScene = nullptr;
PxMaterial* DefaultMaterial = nullptr;
PxControllerManager* ControllerManager = nullptr;
PxCpuDispatcher* CpuDispatcher = nullptr;
PxScene* PhysicsScene = nullptr;
PxMaterial* DefaultMaterial = nullptr;
PxControllerManager* ControllerManager = nullptr;
PxCpuDispatcher* CpuDispatcher = nullptr;
}
bool Physics::AutoSimulation = true;
uint32 Physics::LayerMasks[32];
class PhysicsService : public EngineService
{
@@ -146,6 +158,8 @@ public:
PhysicsService()
: EngineService(TEXT("Physics"), 0)
{
for (int32 i = 0; i < 32; i++)
Physics::LayerMasks[i] = MAX_uint32;
}
bool Init() override;
@@ -155,12 +169,146 @@ public:
PhysicsService PhysicsServiceInstance;
PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled)
{
#if WITH_PVD
PxShapeFlags flags = PxShapeFlag::eVISUALIZATION;
#else
PxShapeFlags flags = static_cast<PxShapeFlags>(0);
#endif
if (isEnabled)
{
if (isTrigger)
{
flags |= PxShapeFlag::eTRIGGER_SHAPE;
if (_queriesHitTriggers)
flags |= PxShapeFlag::eSCENE_QUERY_SHAPE;
}
else
{
flags = PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eSCENE_QUERY_SHAPE;
}
}
return flags;
}
void PhysicsSettings::Apply()
{
Time::_physicsMaxDeltaTime = MaxDeltaTime;
_queriesHitTriggers = QueriesHitTriggers;
_frictionCombineMode = FrictionCombineMode;
_restitutionCombineMode = RestitutionCombineMode;
Platform::MemoryCopy(Physics::LayerMasks, LayerMasks, sizeof(LayerMasks));
Physics::SetGravity(DefaultGravity);
Physics::SetBounceThresholdVelocity(BounceThresholdVelocity);
Physics::SetEnableCCD(!DisableCCD);
// TODO: setting eADAPTIVE_FORCE requires PxScene setup (physx docs: This flag is not mutable, and must be set in PxSceneDesc at scene creation.)
// TODO: update all shapes filter data
// TODO: update all shapes flags
/*
{
get all actors and then:
const PxU32 numShapes = actor->getNbShapes();
PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*numShapes);
actor->getShapes(shapes, numShapes);
for (PxU32 i = 0; i < numShapes; i++)
{
..
}
SAMPLE_FREE(shapes);
}*/
}
PhysicsSettings::PhysicsSettings()
{
for (int32 i = 0; i < 32; i++)
LayerMasks[i] = MAX_uint32;
}
void PhysicsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
DESERIALIZE(DefaultGravity);
DESERIALIZE(TriangleMeshTriangleMinAreaThreshold);
DESERIALIZE(BounceThresholdVelocity);
DESERIALIZE(FrictionCombineMode);
DESERIALIZE(RestitutionCombineMode);
DESERIALIZE(DisableCCD);
DESERIALIZE(EnableAdaptiveForce);
DESERIALIZE(MaxDeltaTime);
DESERIALIZE(EnableSubstepping);
DESERIALIZE(SubstepDeltaTime);
DESERIALIZE(MaxSubsteps);
DESERIALIZE(QueriesHitTriggers);
DESERIALIZE(SupportCookingAtRuntime);
const auto layers = stream.FindMember("LayerMasks");
if (layers != stream.MemberEnd())
{
auto& layersArray = layers->value;
ASSERT(layersArray.IsArray());
for (uint32 i = 0; i < layersArray.Size() && i < 32; i++)
{
LayerMasks[i] = layersArray[i].GetUint();
}
}
}
PhysicalMaterial::PhysicalMaterial()
: _material(nullptr)
{
}
PhysicalMaterial::~PhysicalMaterial()
{
if (_material)
{
Physics::RemoveMaterial(_material);
}
}
PxMaterial* PhysicalMaterial::GetPhysXMaterial()
{
if (_material == nullptr && CPhysX)
{
_material = CPhysX->createMaterial(Friction, Friction, Restitution);
_material->userData = this;
const PhysicsCombineMode useFrictionCombineMode = OverrideFrictionCombineMode ? FrictionCombineMode : _frictionCombineMode;
_material->setFrictionCombineMode(static_cast<PxCombineMode::Enum>(useFrictionCombineMode));
const PhysicsCombineMode useRestitutionCombineMode = OverrideRestitutionCombineMode ? RestitutionCombineMode : _restitutionCombineMode;
_material->setRestitutionCombineMode(static_cast<PxCombineMode::Enum>(useRestitutionCombineMode));
}
return _material;
}
void PhysicalMaterial::UpdatePhysXMaterial()
{
if (_material != nullptr)
{
_material->setStaticFriction(Friction);
_material->setDynamicFriction(Friction);
const PhysicsCombineMode useFrictionCombineMode = OverrideFrictionCombineMode ? FrictionCombineMode : _frictionCombineMode;
_material->setFrictionCombineMode(static_cast<PxCombineMode::Enum>(useFrictionCombineMode));
_material->setRestitution(Restitution);
const PhysicsCombineMode useRestitutionCombineMode = OverrideRestitutionCombineMode ? RestitutionCombineMode : _restitutionCombineMode;
_material->setRestitutionCombineMode(static_cast<PxCombineMode::Enum>(useRestitutionCombineMode));
}
}
bool PhysicsService::Init()
{
#define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return true; }
auto cpuInfo = Platform::GetCPUInfo();
auto settings = PhysicsSettings::Instance();
auto& settings = *PhysicsSettings::Get();
// Send info
LOG(Info, "Setup NVIDIA PhysX {0}.{1}.{2}", PX_PHYSICS_VERSION_MAJOR, PX_PHYSICS_VERSION_MINOR, PX_PHYSICS_VERSION_BUGFIX);
@@ -220,7 +368,7 @@ bool PhysicsService::Init()
#if COMPILE_WITH_PHYSICS_COOKING
#if !USE_EDITOR
if (settings->SupportCookingAtRuntime)
if (settings.SupportCookingAtRuntime)
#endif
{
// Init cooking
@@ -235,16 +383,16 @@ bool PhysicsService::Init()
// Create scene description
PxSceneDesc sceneDesc(CPhysX->getTolerancesScale());
sceneDesc.gravity = C2P(settings->DefaultGravity);
sceneDesc.gravity = C2P(settings.DefaultGravity);
sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS;
//sceneDesc.flags |= PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS; // TODO: set it?
if (!settings->DisableCCD)
if (!settings.DisableCCD)
sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
if (settings->EnableAdaptiveForce)
if (settings.EnableAdaptiveForce)
sceneDesc.flags |= PxSceneFlag::eADAPTIVE_FORCE;
sceneDesc.simulationEventCallback = &EventsCallback;
sceneDesc.filterShader = PhysiXFilterShader;
sceneDesc.bounceThresholdVelocity = settings->BounceThresholdVelocity;
sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity;
if (sceneDesc.cpuDispatcher == nullptr)
{
CpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp<uint32>(cpuInfo.ProcessorCoreCount - 1, 1, 4));
@@ -366,7 +514,7 @@ void Physics::SetGravity(const Vector3& value)
bool Physics::GetEnableCCD()
{
return PhysicsScene ? (PhysicsScene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings_DisableCCD;
return PhysicsScene ? (PhysicsScene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings::Get()->DisableCCD;
}
void Physics::SetEnableCCD(const bool value)
@@ -377,7 +525,7 @@ void Physics::SetEnableCCD(const bool value)
float Physics::GetBounceThresholdVelocity()
{
return PhysicsScene ? PhysicsScene->getBounceThresholdVelocity() : PhysicsSettings_BounceThresholdVelocity;
return PhysicsScene ? PhysicsScene->getBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity;
}
void Physics::SetBounceThresholdVelocity(const float value)
@@ -390,13 +538,13 @@ void Physics::Simulate(float dt)
{
ASSERT(IsInMainThread() && !_isDuringSimulation);
ASSERT(CPhysX);
const auto settings = PhysicsSettings::Instance();
const auto& settings = *PhysicsSettings::Get();
// Flush the old/new objects and the other requests before the simulation
FlushRequests();
// Clamp delta
dt = Math::Clamp(dt, 0.0f, settings->MaxDeltaTime);
dt = Math::Clamp(dt, 0.0f, settings.MaxDeltaTime);
// Prepare util objects
if (ScratchMemory == nullptr)
@@ -407,10 +555,10 @@ void Physics::Simulate(float dt)
{
Stepper = New<FixedStepper>();
}
if (settings->EnableSubstepping)
if (settings.EnableSubstepping)
{
// Use substeps
Stepper->Setup(settings->SubstepDeltaTime, settings->MaxSubsteps);
Stepper->Setup(settings.SubstepDeltaTime, settings.MaxSubsteps);
}
else
{

View File

@@ -155,6 +155,11 @@ public:
/// </summary>
API_PROPERTY() static void SetBounceThresholdVelocity(float value);
/// <summary>
/// The collision layers masks. Used to define layer-based collision detection.
/// </summary>
static uint32 LayerMasks[32];
public:
/// <summary>

View File

@@ -1,82 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "PhysicsSettings.h"
#include "Engine/Serialization/Serialization.h"
#include "Physics.h"
void PhysicsSettings::Apply()
{
// Set simulation parameters
Physics::SetGravity(DefaultGravity);
Physics::SetBounceThresholdVelocity(BounceThresholdVelocity);
Physics::SetEnableCCD(!DisableCCD);
// TODO: setting eADAPTIVE_FORCE requires PxScene setup (physx docs: This flag is not mutable, and must be set in PxSceneDesc at scene creation.)
// Update all shapes
// TODO: update all shapes filter data
// TODO: update all shapes flags
/*
{
get all actors and then:
const PxU32 numShapes = actor->getNbShapes();
PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*numShapes);
actor->getShapes(shapes, numShapes);
for (PxU32 i = 0; i < numShapes; i++)
{
..
}
SAMPLE_FREE(shapes);
}*/
}
void PhysicsSettings::RestoreDefault()
{
DefaultGravity = PhysicsSettings_DefaultGravity;
TriangleMeshTriangleMinAreaThreshold = PhysicsSettings_TriangleMeshTriangleMinAreaThreshold;
BounceThresholdVelocity = PhysicsSettings_BounceThresholdVelocity;
FrictionCombineMode = PhysicsSettings_FrictionCombineMode;
RestitutionCombineMode = PhysicsSettings_RestitutionCombineMode;
DisableCCD = PhysicsSettings_DisableCCD;
EnableAdaptiveForce = PhysicsSettings_EnableAdaptiveForce;
MaxDeltaTime = PhysicsSettings_MaxDeltaTime;
EnableSubstepping = PhysicsSettings_EnableSubstepping;
SubstepDeltaTime = PhysicsSettings_SubstepDeltaTime;
MaxSubsteps = PhysicsSettings_MaxSubsteps;
QueriesHitTriggers = PhysicsSettings_QueriesHitTriggers;
SupportCookingAtRuntime = PhysicsSettings_SupportCookingAtRuntime;
for (int32 i = 0; i < 32; i++)
LayerMasks[i] = MAX_uint32;
}
void PhysicsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
DESERIALIZE(DefaultGravity);
DESERIALIZE(TriangleMeshTriangleMinAreaThreshold);
DESERIALIZE(BounceThresholdVelocity);
DESERIALIZE(FrictionCombineMode);
DESERIALIZE(RestitutionCombineMode);
DESERIALIZE(DisableCCD);
DESERIALIZE(EnableAdaptiveForce);
DESERIALIZE(MaxDeltaTime);
DESERIALIZE(EnableSubstepping);
DESERIALIZE(SubstepDeltaTime);
DESERIALIZE(MaxSubsteps);
DESERIALIZE(QueriesHitTriggers);
DESERIALIZE(SupportCookingAtRuntime);
const auto layers = stream.FindMember("LayerMasks");
if (layers != stream.MemberEnd())
{
auto& layersArray = layers->value;
ASSERT(layersArray.IsArray());
for (uint32 i = 0; i < layersArray.Size() && i < 32; i++)
{
LayerMasks[i] = layersArray[i].GetUint();
}
}
}

View File

@@ -6,105 +6,91 @@
#include "Engine/Core/Math/Vector3.h"
#include "Types.h"
// Default values for the physics settings
#define PhysicsSettings_DefaultGravity Vector3(0, -981.0f, 0)
#define PhysicsSettings_TriangleMeshTriangleMinAreaThreshold (5.0f)
#define PhysicsSettings_BounceThresholdVelocity (200.0f)
#define PhysicsSettings_FrictionCombineMode PhysicsCombineMode::Average
#define PhysicsSettings_RestitutionCombineMode PhysicsCombineMode::Average
#define PhysicsSettings_DisableCCD false
#define PhysicsSettings_EnableAdaptiveForce false
#define PhysicsSettings_MaxDeltaTime (1.0f / 10.0f)
#define PhysicsSettings_EnableSubstepping false
#define PhysicsSettings_SubstepDeltaTime (1.0f / 120.0f)
#define PhysicsSettings_MaxSubsteps 5
#define PhysicsSettings_QueriesHitTriggers true
#define PhysicsSettings_SupportCookingAtRuntime false
/// <summary>
/// Physics simulation settings container.
/// </summary>
/// <seealso cref="SettingsBase{PhysicsSettings}" />
class PhysicsSettings : public SettingsBase<PhysicsSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class FLAXENGINE_API PhysicsSettings : public SettingsBase
{
public:
/// <summary>
/// Initializes a new instance of the <see cref="PhysicsSettings"/> class.
/// </summary>
PhysicsSettings()
{
for (int32 i = 0; i < 32; i++)
LayerMasks[i] = MAX_uint32;
}
DECLARE_SCRIPTING_TYPE_MINIMAL(PhysicsSettings);
public:
/// <summary>
/// The default gravity force value (in cm^2/s).
/// </summary>
Vector3 DefaultGravity = PhysicsSettings_DefaultGravity;
/// <summary>
/// Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable.
/// </summary>
float TriangleMeshTriangleMinAreaThreshold = PhysicsSettings_TriangleMeshTriangleMinAreaThreshold;
/// <summary>
/// Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity
/// </summary>
float BounceThresholdVelocity = PhysicsSettings_BounceThresholdVelocity;
/// <summary>
/// Default friction combine mode, controls how friction is computed for multiple materials.
/// </summary>
PhysicsCombineMode FrictionCombineMode = PhysicsSettings_FrictionCombineMode;
/// <summary>
/// Default restitution combine mode, controls how restitution is computed for multiple materials.
/// </summary>
PhysicsCombineMode RestitutionCombineMode = PhysicsSettings_RestitutionCombineMode;
/// <summary>
/// If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for physx to check it internally.
/// </summary>
bool DisableCCD = PhysicsSettings_DisableCCD;
/// <summary>
/// Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts.
/// </summary>
bool EnableAdaptiveForce = PhysicsSettings_EnableAdaptiveForce;
/// <summary>
/// The maximum allowed delta time (in seconds) for the physics simulation step.
/// </summary>
float MaxDeltaTime = PhysicsSettings_MaxDeltaTime;
/// <summary>
/// Whether to substep the physics simulation.
/// </summary>
bool EnableSubstepping = PhysicsSettings_EnableSubstepping;
/// <summary>
/// Delta time (in seconds) for an individual simulation substep.
/// </summary>
float SubstepDeltaTime = PhysicsSettings_SubstepDeltaTime;
/// <summary>
/// The maximum number of substeps for physics simulation.
/// </summary>
int32 MaxSubsteps = PhysicsSettings_MaxSubsteps;
API_FIELD(Attributes="EditorOrder(0), DefaultValue(typeof(Vector3), \"0,-981.0,0\"), EditorDisplay(\"Simulation\")")
Vector3 DefaultGravity = Vector3(0, -981.0f, 0);
/// <summary>
/// If enabled, any Raycast or other scene query that intersects with a Collider marked as a Trigger will returns with a hit. Individual raycasts can override this behavior.
/// </summary>
bool QueriesHitTriggers = PhysicsSettings_QueriesHitTriggers;
API_FIELD(Attributes="EditorOrder(10), DefaultValue(true), EditorDisplay(\"Framerate\")")
bool QueriesHitTriggers = true;
/// <summary>
/// Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(5.0f), EditorDisplay(\"Simulation\")")
float TriangleMeshTriangleMinAreaThreshold = 5.0f;
/// <summary>
/// Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity
/// </summary>
API_FIELD(Attributes="EditorOrder(30), DefaultValue(200.0f), EditorDisplay(\"Simulation\")")
float BounceThresholdVelocity = 200.0f;
/// <summary>
/// Default friction combine mode, controls how friction is computed for multiple materials.
/// </summary>
API_FIELD(Attributes="EditorOrder(40), DefaultValue(PhysicsCombineMode.Average), EditorDisplay(\"Simulation\")")
PhysicsCombineMode FrictionCombineMode = PhysicsCombineMode::Average;
/// <summary>
/// Default restitution combine mode, controls how restitution is computed for multiple materials.
/// </summary>
API_FIELD(Attributes="EditorOrder(40), DefaultValue(PhysicsCombineMode.Average), EditorDisplay(\"Simulation\")")
PhysicsCombineMode RestitutionCombineMode = PhysicsCombineMode::Average;
/// <summary>
/// If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for physx to check it internally.
/// </summary>
API_FIELD(Attributes="EditorOrder(70), DefaultValue(false), EditorDisplay(\"Simulation\")")
bool DisableCCD = false;
/// <summary>
/// Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts.
/// </summary>
API_FIELD(Attributes="EditorOrder(80), DefaultValue(false), EditorDisplay(\"Simulation\")")
bool EnableAdaptiveForce = false;
/// <summary>
/// The maximum allowed delta time (in seconds) for the physics simulation step.
/// </summary>
API_FIELD(Attributes="EditorOrder(1000), DefaultValue(1.0f / 10.0f), EditorDisplay(\"Framerate\")")
float MaxDeltaTime = 1.0f / 10.0f;
/// <summary>
/// Whether to substep the physics simulation.
/// </summary>
API_FIELD(Attributes="EditorOrder(1005), DefaultValue(false), EditorDisplay(\"Framerate\")")
bool EnableSubstepping = false;
/// <summary>
/// Delta time (in seconds) for an individual simulation substep.
/// </summary>
API_FIELD(Attributes="EditorOrder(1010), DefaultValue(1.0f / 120.0f), EditorDisplay(\"Framerate\")")
float SubstepDeltaTime = 1.0f / 120.0f;
/// <summary>
/// The maximum number of substeps for physics simulation.
/// </summary>
API_FIELD(Attributes="EditorOrder(1020), DefaultValue(5), EditorDisplay(\"Framerate\")")
int32 MaxSubsteps = 5;
/// <summary>
/// Enables support for cooking physical collision shapes geometry at runtime. Use it to enable generating runtime terrain collision or convex mesh colliders.
/// </summary>
bool SupportCookingAtRuntime = PhysicsSettings_SupportCookingAtRuntime;
API_FIELD(Attributes="EditorOrder(1100), DefaultValue(false), EditorDisplay(\"Other\")")
bool SupportCookingAtRuntime = false;
public:
@@ -115,8 +101,17 @@ public:
public:
/// <summary>
/// Initializes a new instance of the <see cref="PhysicsSettings"/> class.
/// </summary>
PhysicsSettings();
/// <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 PhysicsSettings* Get();
// [SettingsBase]
void Apply() override;
void RestoreDefault() override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override;
};

View File

@@ -14,9 +14,6 @@
#include <ThirdParty/PhysX/foundation/PxBounds3.h>
#include <ThirdParty/PhysX/characterkinematic/PxExtended.h>
#include <ThirdParty/PhysX/PxShape.h>
#include "PhysicsSettings.h"
//////////////////////////// Conversion between PhysX and Flax types
inline PxVec2& C2P(const Vector2& v)
{
@@ -43,8 +40,6 @@ inline PxBounds3& C2P(const BoundingBox& v)
return *(PxBounds3*)&v;
}
////////////////////////////
inline Vector2& P2C(const PxVec2& v)
{
return *(Vector2*)&v;
@@ -79,29 +74,4 @@ inline Vector3 P2C(const PxExtendedVec3& v)
#endif
}
////////////////////////////
inline PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled)
{
#if WITH_PVD
PxShapeFlags flags = PxShapeFlag::eVISUALIZATION;
#else
PxShapeFlags flags = static_cast<PxShapeFlags>(0);
#endif
if (isEnabled)
{
if (isTrigger)
{
flags |= PxShapeFlag::eTRIGGER_SHAPE;
if (PhysicsSettings::Instance()->QueriesHitTriggers)
flags |= PxShapeFlag::eSCENE_QUERY_SHAPE;
}
else
{
flags = PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eSCENE_QUERY_SHAPE;
}
}
return flags;
}
extern PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled);

View File

@@ -9,41 +9,37 @@
/// <summary>
/// Android platform settings.
/// </summary>
/// <seealso cref="SettingsBase{AndroidPlatformSettings}" />
class AndroidPlatformSettings : public SettingsBase<AndroidPlatformSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AndroidPlatformSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(AndroidPlatformSettings);
public:
/// <summary>
/// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.
/// </summary>
String PackageName;
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"General\")")
String PackageName = TEXT("com.${COMPANY_NAME}.${PROJECT_NAME}");
/// <summary>
/// The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file.
/// </summary>
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"General\")")
Array<String> Permissions;
/// <summary>
/// Custom icon texture (asset id) to use for the application (overrides the default one).
/// </summary>
API_FIELD(Attributes="EditorOrder(1030), AssetReference(typeof(Texture)), EditorDisplay(\"Other\")")
Guid OverrideIcon;
public:
AndroidPlatformSettings()
{
RestoreDefault();
}
/// <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 AndroidPlatformSettings* Get();
// [SettingsBase]
void RestoreDefault() final override
{
PackageName = TEXT("com.${COMPANY_NAME}.${PROJECT_NAME}");
Permissions.Clear();
OverrideIcon = Guid::Empty;
}
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(PackageName);

View File

@@ -9,66 +9,67 @@
/// <summary>
/// Linux platform settings.
/// </summary>
/// <seealso cref="SettingsBase{LinuxPlatformSettings}" />
class LinuxPlatformSettings : public SettingsBase<LinuxPlatformSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API LinuxPlatformSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(LinuxPlatformSettings);
public:
/// <summary>
/// The default game window mode.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(GameWindowMode.Windowed), EditorDisplay(\"Window\")")
GameWindowMode WindowMode = GameWindowMode::Windowed;
/// <summary>
/// The default game window width (in pixels).
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(1280), EditorDisplay(\"Window\")")
int32 ScreenWidth = 1280;
/// <summary>
/// The default game window height (in pixels).
/// </summary>
API_FIELD(Attributes="EditorOrder(30), DefaultValue(720), EditorDisplay(\"Window\")")
int32 ScreenHeight = 720;
/// <summary>
/// Enables game running when application window loses focus.
/// </summary>
bool RunInBackground = false;
/// <summary>
/// Enables resizing the game window by the user.
/// </summary>
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Window\")")
bool ResizableWindow = false;
/// <summary>
/// Enables game running when application window loses focus.
/// </summary>
API_FIELD(Attributes="EditorOrder(1010), DefaultValue(false), EditorDisplay(\"Other\", \"Run In Background\")")
bool RunInBackground = false;
/// <summary>
/// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.
/// </summary>
API_FIELD(Attributes="EditorOrder(1020), DefaultValue(false), EditorDisplay(\"Other\")")
bool ForceSingleInstance = false;
/// <summary>
/// Custom icon texture (asset id) to use for the application (overrides the default one).
/// </summary>
Guid OverrideIcon = Guid::Empty;
API_FIELD(Attributes="EditorOrder(1030), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.AssetRefEditor\"), AssetReference(typeof(Texture)), EditorDisplay(\"Other\")")
Guid OverrideIcon;
/// <summary>
/// Enables support for Vulkan. Disabling it reduces compiled shaders count.
/// </summary>
API_FIELD(Attributes="EditorOrder(2000), DefaultValue(true), EditorDisplay(\"Graphics\")")
bool SupportVulkan = true;
public:
// [SettingsBase]
void RestoreDefault() final override
{
WindowMode = GameWindowMode::Windowed;
ScreenWidth = 1280;
ScreenHeight = 720;
RunInBackground = false;
ResizableWindow = false;
ForceSingleInstance = false;
OverrideIcon = Guid::Empty;
SupportVulkan = true;
}
/// <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 LinuxPlatformSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(WindowMode);

View File

@@ -62,5 +62,15 @@ public class Platform : EngineModule
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}
if (options.Target.IsEditor)
{
// Include platform settings headers
options.SourceFiles.Add(Path.Combine(FolderPath, "Windows", "WindowsPlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "UWP", "UWPPlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "Linux", "LinuxPlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "Android", "AndroidPlatformSettings.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"));
}
}
}

View File

@@ -9,15 +9,15 @@
/// <summary>
/// Universal Windows Platform settings.
/// </summary>
/// <seealso cref="SettingsBase{UWPPlatformSettings}" />
class UWPPlatformSettings : public SettingsBase<UWPPlatformSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API UWPPlatformSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(UWPPlatformSettings);
public:
/// <summary>
/// The preferred launch windowing mode.
/// </summary>
enum class WindowMode
API_ENUM() enum class WindowMode
{
/// <summary>
/// The full screen mode
@@ -33,7 +33,7 @@ public:
/// <summary>
/// The display orientation modes. Can be combined as flags.
/// </summary>
enum class DisplayOrientations
API_ENUM(Attributes="Flags") enum class DisplayOrientations
{
/// <summary>
/// The none.
@@ -71,40 +71,41 @@ public:
/// <summary>
/// The preferred launch windowing mode. Always fullscreen on Xbox.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(WindowMode.FullScreen), EditorDisplay(\"Window\")")
WindowMode PreferredLaunchWindowingMode = WindowMode::FullScreen;
/// <summary>
/// The display orientation modes. Can be combined as flags.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(DisplayOrientations.All), EditorDisplay(\"Window\")")
DisplayOrientations AutoRotationPreferences = DisplayOrientations::All;
/// <summary>
/// The location of the package certificate (relative to the project).
/// </summary>
API_FIELD(Attributes="EditorOrder(1010), DefaultValue(\"\"), EditorDisplay(\"Other\")")
String CertificateLocation;
/// <summary>
/// Enables support for DirectX 11. Disabling it reduces compiled shaders count.
/// </summary>
API_FIELD(Attributes="EditorOrder(2000), DefaultValue(true), EditorDisplay(\"Graphics\", \"Support DirectX 11\")")
bool SupportDX11 = true;
/// <summary>
/// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.
/// </summary>
API_FIELD(Attributes="EditorOrder(2010), DefaultValue(false), EditorDisplay(\"Graphics\", \"Support DirectX 10\")")
bool SupportDX10 = false;
public:
// [SettingsBase]
void RestoreDefault() final override
{
PreferredLaunchWindowingMode = WindowMode::FullScreen;
AutoRotationPreferences = DisplayOrientations::All;
CertificateLocation.Clear();
SupportDX11 = true;
SupportDX10 = false;
}
/// <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 UWPPlatformSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(PreferredLaunchWindowingMode);

View File

@@ -9,84 +9,85 @@
/// <summary>
/// Windows platform settings.
/// </summary>
/// <seealso cref="SettingsBase{WindowsPlatformSettings}" />
class WindowsPlatformSettings : public SettingsBase<WindowsPlatformSettings>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API WindowsPlatformSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(WindowsPlatformSettings);
public:
/// <summary>
/// The default game window mode.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(GameWindowMode.Windowed), EditorDisplay(\"Window\")")
GameWindowMode WindowMode = GameWindowMode::Windowed;
/// <summary>
/// The default game window width (in pixels).
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(1280), EditorDisplay(\"Window\")")
int32 ScreenWidth = 1280;
/// <summary>
/// The default game window height (in pixels).
/// </summary>
API_FIELD(Attributes="EditorOrder(30), DefaultValue(720), EditorDisplay(\"Window\")")
int32 ScreenHeight = 720;
/// <summary>
/// Enables game running when application window loses focus.
/// </summary>
bool RunInBackground = false;
/// <summary>
/// Enables resizing the game window by the user.
/// </summary>
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Window\")")
bool ResizableWindow = false;
/// <summary>
/// Enables game running when application window loses focus.
/// </summary>
API_FIELD(Attributes="EditorOrder(1010), DefaultValue(false), EditorDisplay(\"Other\", \"Run In Background\")")
bool RunInBackground = false;
/// <summary>
/// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.
/// </summary>
API_FIELD(Attributes="EditorOrder(1020), DefaultValue(false), EditorDisplay(\"Other\")")
bool ForceSingleInstance = false;
/// <summary>
/// Custom icon texture (asset id) to use for the application (overrides the default one).
/// </summary>
API_FIELD(Attributes="EditorOrder(1030), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.AssetRefEditor\"), AssetReference(typeof(Texture)), EditorDisplay(\"Other\")")
Guid OverrideIcon;
/// <summary>
/// Enables support for DirectX 12. Disabling it reduces compiled shaders count.
/// </summary>
API_FIELD(Attributes="EditorOrder(2000), DefaultValue(false), EditorDisplay(\"Graphics\", \"Support DirectX 12\")")
bool SupportDX12 = false;
/// <summary>
/// Enables support for DirectX 11. Disabling it reduces compiled shaders count.
/// </summary>
API_FIELD(Attributes="EditorOrder(2010), DefaultValue(true), EditorDisplay(\"Graphics\", \"Support DirectX 11\")")
bool SupportDX11 = true;
/// <summary>
/// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.
/// </summary>
API_FIELD(Attributes="EditorOrder(2020), DefaultValue(false), EditorDisplay(\"Graphics\", \"Support DirectX 10\")")
bool SupportDX10 = false;
/// <summary>
/// Enables support for Vulkan. Disabling it reduces compiled shaders count.
/// </summary>
API_FIELD(Attributes="EditorOrder(2030), DefaultValue(false), EditorDisplay(\"Graphics\")")
bool SupportVulkan = false;
public:
// [SettingsBase]
void RestoreDefault() final override
{
WindowMode = GameWindowMode::Windowed;
ScreenWidth = 1280;
ScreenHeight = 720;
RunInBackground = false;
ResizableWindow = false;
ForceSingleInstance = false;
OverrideIcon = Guid::Empty;
SupportDX12 = false;
SupportDX11 = true;
SupportDX10 = false;
SupportVulkan = false;
}
/// <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 WindowsPlatformSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
DESERIALIZE(WindowMode);

View File

@@ -69,7 +69,7 @@ void Terrain::UpdateLayerBits()
filterData.word0 = GetLayerMask();
// Own layer mask
filterData.word1 = PhysicsSettings::Instance()->LayerMasks[GetLayer()];
filterData.word1 = Physics::LayerMasks[GetLayer()];
// Update the shapes layer bits
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)