Merge branch 'FlaxEngine:master' into terrainscripting
This commit is contained in:
@@ -114,14 +114,19 @@ void Behavior::UpdateAsync()
|
||||
|
||||
void Behavior::StartLogic()
|
||||
{
|
||||
if (_result == BehaviorUpdateResult::Running)
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
|
||||
// Ensure to have tree loaded on begin play
|
||||
// Ensure to have tree loaded on play
|
||||
CHECK(Tree && !Tree->WaitForLoaded());
|
||||
BehaviorTree* tree = Tree.Get();
|
||||
CHECK(tree->Graph.Root);
|
||||
|
||||
// Setup state
|
||||
_result = BehaviorUpdateResult::Running;
|
||||
_accumulatedTime = 0.0f;
|
||||
_totalTime = 0;
|
||||
|
||||
// Init knowledge
|
||||
_knowledge.InitMemory(tree);
|
||||
@@ -135,6 +140,7 @@ void Behavior::StopLogic(BehaviorUpdateResult result)
|
||||
_accumulatedTime = 0.0f;
|
||||
_totalTime = 0;
|
||||
_result = result;
|
||||
_knowledge.FreeMemory();
|
||||
}
|
||||
|
||||
void Behavior::ResetLogic()
|
||||
@@ -170,7 +176,11 @@ void Behavior::OnDisable()
|
||||
|
||||
bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior)
|
||||
{
|
||||
return node && behavior && node->_executionIndex != -1 && behavior->_knowledge.RelevantNodes.Get(node->_executionIndex);
|
||||
return node &&
|
||||
behavior &&
|
||||
node->_executionIndex >= 0 &&
|
||||
node->_executionIndex < behavior->_knowledge.RelevantNodes.Count() &&
|
||||
behavior->_knowledge.RelevantNodes.Get(node->_executionIndex);
|
||||
}
|
||||
|
||||
String Behavior::GetNodeDebugInfo(const BehaviorTreeNode* node, Behavior* behavior)
|
||||
@@ -179,7 +189,7 @@ String Behavior::GetNodeDebugInfo(const BehaviorTreeNode* node, Behavior* behavi
|
||||
return String::Empty;
|
||||
BehaviorUpdateContext context;
|
||||
Platform::MemoryClear(&context, sizeof(context));
|
||||
if (behavior && node->_executionIndex != -1 && behavior->_knowledge.RelevantNodes.Get(node->_executionIndex))
|
||||
if (GetNodeDebugRelevancy(node, behavior))
|
||||
{
|
||||
// Pass behavior and knowledge data only for relevant nodes to properly access it
|
||||
context.Behavior = behavior;
|
||||
|
||||
@@ -83,7 +83,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
else if (typeName.HasChars())
|
||||
{
|
||||
LOG(Warning, "Missing scripting type \'{0}\'", String(typeName));
|
||||
}
|
||||
@@ -150,7 +150,13 @@ void BehaviorKnowledge::InitMemory(BehaviorTree* tree)
|
||||
RelevantNodes.Resize(tree->Graph.NodesCount, false);
|
||||
RelevantNodes.SetAll(false);
|
||||
if (!Memory && tree->Graph.NodesStatesSize)
|
||||
{
|
||||
Memory = Allocator::Allocate(tree->Graph.NodesStatesSize);
|
||||
#if !BUILD_RELEASE
|
||||
// Clear memory to make it easier to spot missing data issues (eg. zero GCHandle in C# BT node due to missing state init)
|
||||
Platform::MemoryClear(Memory, tree->Graph.NodesStatesSize);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void BehaviorKnowledge::FreeMemory()
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace FlaxEngine
|
||||
public T Get(BehaviorKnowledge knowledge)
|
||||
{
|
||||
if (knowledge != null && knowledge.Get(Path, out var value))
|
||||
return (T)value;
|
||||
return Utilities.VariantUtils.Cast<T>(value);
|
||||
return default;
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace FlaxEngine
|
||||
object tmp = null;
|
||||
bool result = knowledge != null && knowledge.Get(Path, out tmp);
|
||||
if (result)
|
||||
value = (T)tmp;
|
||||
value = Utilities.VariantUtils.Cast<T>(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,12 +95,16 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T GetState<T>(IntPtr memory) where T : struct
|
||||
{
|
||||
var ptr = IntPtr.Add(memory, _memoryOffset).ToPointer();
|
||||
var handle = GCHandle.FromIntPtr(Unsafe.Read<IntPtr>(ptr));
|
||||
var ptr = Unsafe.Read<IntPtr>(IntPtr.Add(memory, _memoryOffset).ToPointer());
|
||||
#if !BUILD_RELEASE
|
||||
if (ptr == IntPtr.Zero)
|
||||
throw new Exception($"Missing state '{typeof(T).FullName}' for node '{GetType().FullName}'");
|
||||
#endif
|
||||
var handle = GCHandle.FromIntPtr(ptr);
|
||||
var state = handle.Target;
|
||||
#if !BUILD_RELEASE
|
||||
if (state == null)
|
||||
throw new NullReferenceException();
|
||||
throw new Exception($"Missing state '{typeof(T).FullName}' for node '{GetType().FullName}'");
|
||||
#endif
|
||||
return ref Unsafe.Unbox<T>(state);
|
||||
}
|
||||
@@ -111,8 +115,10 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void FreeState(IntPtr memory)
|
||||
{
|
||||
var ptr = IntPtr.Add(memory, _memoryOffset).ToPointer();
|
||||
var handle = GCHandle.FromIntPtr(Unsafe.Read<IntPtr>(ptr));
|
||||
var ptr = Unsafe.Read<IntPtr>(IntPtr.Add(memory, _memoryOffset).ToPointer());
|
||||
if (ptr == IntPtr.Zero)
|
||||
return;
|
||||
var handle = GCHandle.FromIntPtr(ptr);
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ BehaviorUpdateResult BehaviorTreeNode::InvokeUpdate(const BehaviorUpdateContext&
|
||||
result = BehaviorUpdateResult::Failed;
|
||||
else
|
||||
result = Update(context);
|
||||
if ((int32)result < 0 || (int32)result > (int32)BehaviorUpdateResult::Failed)
|
||||
result = BehaviorUpdateResult::Failed; // Invalid value is a failure
|
||||
|
||||
// Post-process result from decorators
|
||||
for (BehaviorTreeDecorator* decorator : _decorators)
|
||||
|
||||
@@ -1342,7 +1342,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
const bool xAxis = Math::IsZero(v0.X) && Math::IsZero(v1.X);
|
||||
const bool yAxis = Math::IsZero(v0.Y) && Math::IsZero(v1.Y);
|
||||
if (xAxis || yAxis)
|
||||
if (xAxis && yAxis)
|
||||
{
|
||||
// Single animation
|
||||
value = SampleAnimation(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, aData.W);
|
||||
}
|
||||
else if (xAxis || yAxis)
|
||||
{
|
||||
if (yAxis)
|
||||
{
|
||||
@@ -2109,7 +2114,8 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
bucket.LoopsLeft--;
|
||||
bucket.LoopsDone++;
|
||||
}
|
||||
value = SampleAnimation(node, loop, length, 0.0f, bucket.TimePosition, newTimePos, anim, slot.Speed);
|
||||
// Speed is accounted for in the new time pos, so keep sample speed at 1
|
||||
value = SampleAnimation(node, loop, length, 0.0f, bucket.TimePosition, newTimePos, anim, 1);
|
||||
bucket.TimePosition = newTimePos;
|
||||
if (bucket.LoopsLeft == 0 && slot.BlendOutTime > 0.0f && length - slot.BlendOutTime < bucket.TimePosition)
|
||||
{
|
||||
|
||||
@@ -225,6 +225,7 @@ bool AudioClip::ExtractDataRaw(Array<byte>& resultData, AudioDataInfo& resultDat
|
||||
|
||||
void AudioClip::CancelStreaming()
|
||||
{
|
||||
Asset::CancelStreaming();
|
||||
CancelStreamingTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
AssetReferenceBase::~AssetReferenceBase()
|
||||
{
|
||||
if (_asset)
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
|
||||
_asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
|
||||
_asset->RemoveReference();
|
||||
_asset = nullptr;
|
||||
asset->OnLoaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnLoaded>(this);
|
||||
asset->OnUnloaded.Unbind<AssetReferenceBase, &AssetReferenceBase::OnUnloaded>(this);
|
||||
asset->RemoveReference();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +72,12 @@ void AssetReferenceBase::OnUnloaded(Asset* asset)
|
||||
|
||||
WeakAssetReferenceBase::~WeakAssetReferenceBase()
|
||||
{
|
||||
if (_asset)
|
||||
_asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset = nullptr;
|
||||
asset->OnUnloaded.Unbind<WeakAssetReferenceBase, &WeakAssetReferenceBase::OnUnloaded>(this);
|
||||
}
|
||||
}
|
||||
|
||||
String WeakAssetReferenceBase::ToString() const
|
||||
@@ -101,6 +107,20 @@ void WeakAssetReferenceBase::OnUnloaded(Asset* asset)
|
||||
_asset = nullptr;
|
||||
}
|
||||
|
||||
SoftAssetReferenceBase::~SoftAssetReferenceBase()
|
||||
{
|
||||
Asset* asset = _asset;
|
||||
if (asset)
|
||||
{
|
||||
_asset = nullptr;
|
||||
asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
|
||||
asset->RemoveReference();
|
||||
}
|
||||
#if !BUILD_RELEASE
|
||||
_id = Guid::Empty;
|
||||
#endif
|
||||
}
|
||||
|
||||
String SoftAssetReferenceBase::ToString() const
|
||||
{
|
||||
return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>"));
|
||||
@@ -502,6 +522,14 @@ void Asset::InitAsVirtual()
|
||||
|
||||
void Asset::CancelStreaming()
|
||||
{
|
||||
// Cancel loading task but go over asset locker to prevent case if other load threads still loads asset while it's reimported on other thread
|
||||
Locker.Lock();
|
||||
ContentLoadTask* loadTask = _loadingTask;
|
||||
Locker.Unlock();
|
||||
if (loadTask)
|
||||
{
|
||||
loadTask->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
/// </summary>
|
||||
class FLAXENGINE_API AssetReferenceBase
|
||||
{
|
||||
public:
|
||||
typedef Delegate<> EventType;
|
||||
|
||||
protected:
|
||||
Asset* _asset = nullptr;
|
||||
|
||||
@@ -19,17 +16,17 @@ public:
|
||||
/// <summary>
|
||||
/// The asset loaded event (fired when asset gets loaded or is already loaded after change).
|
||||
/// </summary>
|
||||
EventType Loaded;
|
||||
Action Loaded;
|
||||
|
||||
/// <summary>
|
||||
/// The asset unloading event (should cleanup refs to it).
|
||||
/// </summary>
|
||||
EventType Unload;
|
||||
Action Unload;
|
||||
|
||||
/// <summary>
|
||||
/// Action fired when field gets changed (link a new asset or change to the another value).
|
||||
/// </summary>
|
||||
EventType Changed;
|
||||
Action Changed;
|
||||
|
||||
public:
|
||||
NON_COPYABLE(AssetReferenceBase);
|
||||
|
||||
@@ -783,6 +783,7 @@ void Model::InitAsVirtual()
|
||||
|
||||
void Model::CancelStreaming()
|
||||
{
|
||||
Asset::CancelStreaming();
|
||||
CancelStreamingTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -969,6 +969,7 @@ void SkinnedModel::InitAsVirtual()
|
||||
|
||||
void SkinnedModel::CancelStreaming()
|
||||
{
|
||||
Asset::CancelStreaming();
|
||||
CancelStreamingTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -1428,6 +1428,10 @@ Asset::LoadResult VisualScript::load()
|
||||
#if USE_EDITOR
|
||||
if (_instances.HasItems())
|
||||
{
|
||||
// Mark as already loaded so any WaitForLoaded checks during GetDefaultInstance bellow will handle this Visual Script as ready to use
|
||||
_loadFailed = false;
|
||||
_isLoaded = true;
|
||||
|
||||
// Setup scripting type
|
||||
CacheScriptingType();
|
||||
|
||||
@@ -1512,7 +1516,7 @@ void VisualScript::unload(bool isReloading)
|
||||
// Note: preserve the registered scripting type but invalidate the locally cached handle
|
||||
if (_scriptingTypeHandle)
|
||||
{
|
||||
VisualScriptingModule.Locker.Lock();
|
||||
VisualScriptingBinaryModule::Locker.Lock();
|
||||
auto& type = VisualScriptingModule.Types[_scriptingTypeHandle.TypeIndex];
|
||||
if (type.Script.DefaultInstance)
|
||||
{
|
||||
@@ -1523,7 +1527,7 @@ void VisualScript::unload(bool isReloading)
|
||||
VisualScriptingModule.Scripts[_scriptingTypeHandle.TypeIndex] = nullptr;
|
||||
_scriptingTypeHandleCached = _scriptingTypeHandle;
|
||||
_scriptingTypeHandle = ScriptingTypeHandle();
|
||||
VisualScriptingModule.Locker.Unlock();
|
||||
VisualScriptingBinaryModule::Locker.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1534,8 +1538,8 @@ AssetChunksFlag VisualScript::getChunksToPreload() const
|
||||
|
||||
void VisualScript::CacheScriptingType()
|
||||
{
|
||||
ScopeLock lock(VisualScriptingBinaryModule::Locker);
|
||||
auto& binaryModule = VisualScriptingModule;
|
||||
ScopeLock lock(binaryModule.Locker);
|
||||
|
||||
// Find base type
|
||||
const StringAnsi baseTypename(Meta.BaseTypename);
|
||||
|
||||
@@ -323,26 +323,29 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool
|
||||
{
|
||||
// Ensure path is in a valid format
|
||||
String pathNorm(path);
|
||||
FileSystem::NormalizePath(pathNorm);
|
||||
ContentStorageManager::FormatPath(pathNorm);
|
||||
const StringView filePath = pathNorm;
|
||||
|
||||
// Find target storage container and the asset
|
||||
auto storage = ContentStorageManager::TryGetStorage(pathNorm);
|
||||
auto asset = Content::GetAsset(pathNorm);
|
||||
auto storage = ContentStorageManager::TryGetStorage(filePath);
|
||||
auto asset = Content::GetAsset(filePath);
|
||||
auto binaryAsset = dynamic_cast<BinaryAsset*>(asset);
|
||||
if (asset && !binaryAsset)
|
||||
{
|
||||
LOG(Warning, "Cannot write to the non-binary asset location.");
|
||||
return true;
|
||||
}
|
||||
if (!binaryAsset && !storage && FileSystem::FileExists(filePath))
|
||||
{
|
||||
// Force-resolve storage (asset at that path could be not yet loaded into registry)
|
||||
storage = ContentStorageManager::GetStorage(filePath);
|
||||
}
|
||||
|
||||
// Check if can perform write operation to the asset container
|
||||
if (storage)
|
||||
if (storage && !storage->AllowDataModifications())
|
||||
{
|
||||
if (!storage->AllowDataModifications())
|
||||
{
|
||||
LOG(Warning, "Cannot write to the asset storage container.");
|
||||
return true;
|
||||
}
|
||||
LOG(Warning, "Cannot write to the asset storage container.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize data container
|
||||
@@ -352,6 +355,11 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool
|
||||
// Use the same asset ID
|
||||
data.Header.ID = binaryAsset->GetID();
|
||||
}
|
||||
else if (storage && storage->GetEntriesCount())
|
||||
{
|
||||
// Use the same file ID
|
||||
data.Header.ID = storage->GetEntry(0).ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Randomize ID
|
||||
@@ -373,8 +381,8 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(pathNorm.HasChars());
|
||||
result = FlaxStorage::Create(pathNorm, data, silentMode);
|
||||
ASSERT(filePath.HasChars());
|
||||
result = FlaxStorage::Create(filePath, data, silentMode);
|
||||
}
|
||||
if (binaryAsset)
|
||||
binaryAsset->_isSaving = false;
|
||||
|
||||
@@ -54,8 +54,7 @@ namespace
|
||||
// Assets
|
||||
CriticalSection AssetsLocker;
|
||||
Dictionary<Guid, Asset*> Assets(2048);
|
||||
CriticalSection LoadCallAssetsLocker;
|
||||
Array<Guid> LoadCallAssets(64);
|
||||
Array<Guid> LoadCallAssets(PLATFORM_THREADS_LIMIT);
|
||||
CriticalSection LoadedAssetsToInvokeLocker;
|
||||
Array<Asset*> LoadedAssetsToInvoke(64);
|
||||
Array<Asset*> ToUnload;
|
||||
@@ -449,18 +448,19 @@ Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& typ
|
||||
{
|
||||
// Ensure path is in a valid format
|
||||
String pathNorm(path);
|
||||
StringUtils::PathRemoveRelativeParts(pathNorm);
|
||||
ContentStorageManager::FormatPath(pathNorm);
|
||||
const StringView filePath = pathNorm;
|
||||
|
||||
#if USE_EDITOR
|
||||
if (!FileSystem::FileExists(pathNorm))
|
||||
if (!FileSystem::FileExists(filePath))
|
||||
{
|
||||
LOG(Error, "Missing file \'{0}\'", pathNorm);
|
||||
LOG(Error, "Missing file \'{0}\'", filePath);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
AssetInfo assetInfo;
|
||||
if (GetAssetInfo(pathNorm, assetInfo))
|
||||
if (GetAssetInfo(filePath, assetInfo))
|
||||
{
|
||||
return LoadAsync(assetInfo.ID, type);
|
||||
}
|
||||
@@ -910,9 +910,13 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
return nullptr;
|
||||
|
||||
// Check if asset has been already loaded
|
||||
Asset* result = GetAsset(id);
|
||||
Asset* result = nullptr;
|
||||
AssetsLocker.Lock();
|
||||
Assets.TryGet(id, result);
|
||||
if (result)
|
||||
{
|
||||
AssetsLocker.Unlock();
|
||||
|
||||
// Validate type
|
||||
if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type))
|
||||
{
|
||||
@@ -923,57 +927,41 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
|
||||
}
|
||||
|
||||
// Check if that asset is during loading
|
||||
LoadCallAssetsLocker.Lock();
|
||||
if (LoadCallAssets.Contains(id))
|
||||
{
|
||||
LoadCallAssetsLocker.Unlock();
|
||||
AssetsLocker.Unlock();
|
||||
|
||||
// Wait for load end
|
||||
// TODO: dont use active waiting and prevent deadlocks if running on a main thread
|
||||
//while (!Engine::ShouldExit())
|
||||
while (true)
|
||||
// Wait for loading end by other thread
|
||||
bool contains = true;
|
||||
while (contains)
|
||||
{
|
||||
LoadCallAssetsLocker.Lock();
|
||||
const bool contains = LoadCallAssets.Contains(id);
|
||||
LoadCallAssetsLocker.Unlock();
|
||||
if (!contains)
|
||||
return GetAsset(id);
|
||||
Platform::Sleep(1);
|
||||
AssetsLocker.Lock();
|
||||
contains = LoadCallAssets.Contains(id);
|
||||
AssetsLocker.Unlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mark asset as loading
|
||||
LoadCallAssets.Add(id);
|
||||
LoadCallAssetsLocker.Unlock();
|
||||
Assets.TryGet(id, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Load asset
|
||||
AssetInfo assetInfo;
|
||||
result = load(id, type, assetInfo);
|
||||
// Mark asset as loading and release lock so other threads can load other assets
|
||||
LoadCallAssets.Add(id);
|
||||
AssetsLocker.Unlock();
|
||||
|
||||
// End loading
|
||||
LoadCallAssetsLocker.Lock();
|
||||
LoadCallAssets.Remove(id);
|
||||
LoadCallAssetsLocker.Unlock();
|
||||
#define LOAD_FAILED() AssetsLocker.Lock(); LoadCallAssets.Remove(id); AssetsLocker.Unlock(); return nullptr
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& assetInfo)
|
||||
{
|
||||
// Get cached asset info (from registry)
|
||||
AssetInfo assetInfo;
|
||||
if (!GetAssetInfo(id, assetInfo))
|
||||
{
|
||||
LOG(Warning, "Invalid or missing asset ({0}, {1}).", id, type.ToString());
|
||||
return nullptr;
|
||||
LOAD_FAILED();
|
||||
}
|
||||
|
||||
#if ASSETS_LOADING_EXTRA_VERIFICATION
|
||||
if (!FileSystem::FileExists(assetInfo.Path))
|
||||
{
|
||||
LOG(Error, "Cannot find file '{0}'", assetInfo.Path);
|
||||
return nullptr;
|
||||
LOAD_FAILED();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -982,28 +970,27 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo&
|
||||
if (factory == nullptr)
|
||||
{
|
||||
LOG(Error, "Cannot find asset factory. Info: {0}", assetInfo.ToString());
|
||||
return nullptr;
|
||||
LOAD_FAILED();
|
||||
}
|
||||
|
||||
// Create asset object
|
||||
auto result = factory->New(assetInfo);
|
||||
result = factory->New(assetInfo);
|
||||
if (result == nullptr)
|
||||
{
|
||||
LOG(Error, "Cannot create asset object. Info: {0}", assetInfo.ToString());
|
||||
return nullptr;
|
||||
LOAD_FAILED();
|
||||
}
|
||||
|
||||
ASSERT(result->GetID() == id);
|
||||
#if ASSETS_LOADING_EXTRA_VERIFICATION
|
||||
if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type))
|
||||
{
|
||||
LOG(Error, "Different loaded asset type! Asset: '{0}'. Expected type: {1}", assetInfo.ToString(), type.ToString());
|
||||
LOG(Warning, "Different loaded asset type! Asset: '{0}'. Expected type: {1}", assetInfo.ToString(), type.ToString());
|
||||
result->DeleteObject();
|
||||
return nullptr;
|
||||
LOAD_FAILED();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Register asset
|
||||
ASSERT(result->GetID() == id);
|
||||
AssetsLocker.Lock();
|
||||
#if ASSETS_LOADING_EXTRA_VERIFICATION
|
||||
ASSERT(!Assets.ContainsKey(id));
|
||||
@@ -1011,11 +998,14 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo&
|
||||
Assets.Add(id, result);
|
||||
|
||||
// Start asset loading
|
||||
// TODO: refactor this to create asset loading task-chain before AssetsLocker.Lock() to allow better parallelization
|
||||
result->startLoading();
|
||||
|
||||
// Remove from the loading queue and release lock
|
||||
LoadCallAssets.Remove(id);
|
||||
AssetsLocker.Unlock();
|
||||
|
||||
#undef LOAD_FAILED
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -366,7 +366,6 @@ private:
|
||||
static void onAssetLoaded(Asset* asset);
|
||||
static void onAssetUnload(Asset* asset);
|
||||
static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId);
|
||||
static Asset* load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& assetInfo);
|
||||
|
||||
private:
|
||||
static void deleteFileSafety(const StringView& path, const Guid& id);
|
||||
|
||||
@@ -48,6 +48,8 @@ protected:
|
||||
// [ContentLoadTask]
|
||||
Result run() override
|
||||
{
|
||||
if (IsCancelRequested())
|
||||
return Result::Ok;
|
||||
PROFILE_CPU();
|
||||
|
||||
AssetReference<BinaryAsset> ref = _asset.Get();
|
||||
@@ -67,8 +69,6 @@ protected:
|
||||
{
|
||||
if (IsCancelRequested())
|
||||
return Result::Ok;
|
||||
|
||||
// Load it
|
||||
#if TRACY_ENABLE
|
||||
ZoneScoped;
|
||||
ZoneName(*name, name.Length());
|
||||
|
||||
@@ -30,9 +30,7 @@ public:
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="SoftAssetReferenceBase"/> class.
|
||||
/// </summary>
|
||||
~SoftAssetReferenceBase()
|
||||
{
|
||||
}
|
||||
~SoftAssetReferenceBase();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/TaskGraph.h"
|
||||
@@ -185,6 +186,16 @@ void ContentStorageManager::EnsureUnlocked()
|
||||
Locker.Unlock();
|
||||
}
|
||||
|
||||
void ContentStorageManager::FormatPath(String& path)
|
||||
{
|
||||
StringUtils::PathRemoveRelativeParts(path);
|
||||
if (FileSystem::IsRelative(path))
|
||||
{
|
||||
// Convert local-project paths into absolute format which is used by Content Storage system
|
||||
path = Globals::ProjectFolder / path;
|
||||
}
|
||||
}
|
||||
|
||||
bool ContentStorageManager::IsFlaxStoragePath(const String& path)
|
||||
{
|
||||
auto extension = FileSystem::GetExtension(path).ToLower();
|
||||
|
||||
@@ -75,6 +75,9 @@ public:
|
||||
/// </summary>
|
||||
static void EnsureUnlocked();
|
||||
|
||||
// Formats path into valid format used by the storage system (normalized and absolute).
|
||||
static void FormatPath(String& path);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Determines whether the specified path can be a binary asset file (based on it's extension).
|
||||
|
||||
@@ -1302,15 +1302,15 @@ void FlaxStorage::CloseFileHandles()
|
||||
// In those situations all the async tasks using this storage should be cancelled externally
|
||||
|
||||
// Ensure that no one is using this resource
|
||||
int32 waitTime = 10;
|
||||
int32 waitTime = 100;
|
||||
while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0)
|
||||
Platform::Sleep(10);
|
||||
Platform::Sleep(1);
|
||||
if (Platform::AtomicRead(&_chunksLock) != 0)
|
||||
{
|
||||
// File can be locked by some streaming tasks (eg. AudioClip::StreamingTask or StreamModelLODTask)
|
||||
Entry e;
|
||||
for (int32 i = 0; i < GetEntriesCount(); i++)
|
||||
{
|
||||
Entry e;
|
||||
GetEntry(i, e);
|
||||
Asset* asset = Content::GetAsset(e.ID);
|
||||
if (asset)
|
||||
@@ -1320,8 +1320,12 @@ void FlaxStorage::CloseFileHandles()
|
||||
}
|
||||
}
|
||||
}
|
||||
waitTime = 100;
|
||||
while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0)
|
||||
Platform::Sleep(1);
|
||||
ASSERT(_chunksLock == 0);
|
||||
|
||||
// Close file handles (from all threads)
|
||||
_file.DeleteAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,19 @@ private:
|
||||
int32 _capacity;
|
||||
AllocationData _allocation;
|
||||
|
||||
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromCount, int32 fromCapacity)
|
||||
{
|
||||
if IF_CONSTEXPR (AllocationType::HasSwap)
|
||||
to.Swap(from);
|
||||
else
|
||||
{
|
||||
to.Allocate(fromCapacity);
|
||||
Memory::MoveItems(to.Get(), from.Get(), fromCount);
|
||||
Memory::DestructItems(from.Get(), fromCount);
|
||||
from.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Array"/> class.
|
||||
@@ -134,7 +147,7 @@ public:
|
||||
_capacity = other._capacity;
|
||||
other._count = 0;
|
||||
other._capacity = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
MoveToEmpty(_allocation, other._allocation, _count, _capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,7 +204,7 @@ public:
|
||||
_capacity = other._capacity;
|
||||
other._count = 0;
|
||||
other._capacity = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
MoveToEmpty(_allocation, other._allocation, _count, _capacity);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -713,9 +726,18 @@ public:
|
||||
/// <param name="other">The other collection.</param>
|
||||
void Swap(Array& other)
|
||||
{
|
||||
::Swap(_count, other._count);
|
||||
::Swap(_capacity, other._capacity);
|
||||
_allocation.Swap(other._allocation);
|
||||
if IF_CONSTEXPR (AllocationType::HasSwap)
|
||||
{
|
||||
_allocation.Swap(other._allocation);
|
||||
::Swap(_count, other._count);
|
||||
::Swap(_capacity, other._capacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array tmp = MoveTemp(other);
|
||||
other = *this;
|
||||
*this = MoveTemp(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -726,9 +748,7 @@ public:
|
||||
T* data = _allocation.Get();
|
||||
const int32 count = _count / 2;
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
::Swap(data[i], data[_count - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -22,6 +22,16 @@ private:
|
||||
int32 _capacity;
|
||||
AllocationData _allocation;
|
||||
|
||||
FORCE_INLINE static int32 ToItemCount(int32 size)
|
||||
{
|
||||
return Math::DivideAndRoundUp<int32>(size, sizeof(ItemType));
|
||||
}
|
||||
|
||||
FORCE_INLINE static int32 ToItemCapacity(int32 size)
|
||||
{
|
||||
return Math::Max<int32>(Math::DivideAndRoundUp<int32>(size, sizeof(ItemType)), 1);
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BitArray"/> class.
|
||||
@@ -41,7 +51,7 @@ public:
|
||||
, _capacity(capacity)
|
||||
{
|
||||
if (capacity > 0)
|
||||
_allocation.Allocate(Math::Max<ItemType>(capacity / sizeof(ItemType), 1));
|
||||
_allocation.Allocate(ToItemCapacity(capacity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,7 +63,7 @@ public:
|
||||
_count = _capacity = other.Count();
|
||||
if (_capacity > 0)
|
||||
{
|
||||
const uint64 itemsCapacity = Math::Max<ItemType>(_capacity / sizeof(ItemType), 1);
|
||||
const int32 itemsCapacity = ToItemCapacity(_capacity);
|
||||
_allocation.Allocate(itemsCapacity);
|
||||
Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType));
|
||||
}
|
||||
@@ -69,7 +79,7 @@ public:
|
||||
_count = _capacity = other.Count();
|
||||
if (_capacity > 0)
|
||||
{
|
||||
const uint64 itemsCapacity = Math::Max<ItemType>(_capacity / sizeof(ItemType), 1);
|
||||
const int32 itemsCapacity = ToItemCapacity(_capacity);
|
||||
_allocation.Allocate(itemsCapacity);
|
||||
Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType));
|
||||
}
|
||||
@@ -101,7 +111,7 @@ public:
|
||||
{
|
||||
_allocation.Free();
|
||||
_capacity = other._count;
|
||||
const uint64 itemsCapacity = Math::Max<ItemType>(_capacity / sizeof(ItemType), 1);
|
||||
const int32 itemsCapacity = ToItemCapacity(_capacity);
|
||||
_allocation.Allocate(itemsCapacity);
|
||||
Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType));
|
||||
}
|
||||
@@ -246,7 +256,7 @@ public:
|
||||
return;
|
||||
ASSERT(capacity >= 0);
|
||||
const int32 count = preserveContents ? (_count < capacity ? _count : capacity) : 0;
|
||||
_allocation.Relocate(Math::Max<ItemType>(capacity / sizeof(ItemType), 1), _count / sizeof(ItemType), count / sizeof(ItemType));
|
||||
_allocation.Relocate(ToItemCapacity(capacity), ToItemCount(_count), ToItemCount(count));
|
||||
_capacity = capacity;
|
||||
_count = count;
|
||||
}
|
||||
@@ -272,7 +282,7 @@ public:
|
||||
{
|
||||
if (_capacity < minCapacity)
|
||||
{
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(Math::Max<int32>(_capacity / sizeof(ItemType), 1), minCapacity);
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(ToItemCapacity(_capacity), minCapacity);
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
}
|
||||
@@ -284,7 +294,7 @@ public:
|
||||
void SetAll(const bool value)
|
||||
{
|
||||
if (_count != 0)
|
||||
Platform::MemorySet(_allocation.Get(), Math::Max<ItemType>(_count / sizeof(ItemType), 1), value ? MAX_int32 : 0);
|
||||
Platform::MemorySet(_allocation.Get(), ToItemCount(_count) * sizeof(ItemType), value ? MAX_uint32 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,13 +2,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
/// <summary>
|
||||
/// Default capacity for the dictionaries (amount of space for the elements)
|
||||
/// </summary>
|
||||
#define DICTIONARY_DEFAULT_CAPACITY 256
|
||||
#include "Engine/Platform/Defines.h"
|
||||
|
||||
/// <summary>
|
||||
/// Function for dictionary that tells how change hash index during iteration (size param is a buckets table size)
|
||||
/// Default capacity for the dictionaries (amount of space for the elements).
|
||||
/// </summary>
|
||||
#ifndef DICTIONARY_DEFAULT_CAPACITY
|
||||
#if PLATFORM_DESKTOP
|
||||
#define DICTIONARY_DEFAULT_CAPACITY 256
|
||||
#else
|
||||
#define DICTIONARY_DEFAULT_CAPACITY 64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Default slack space divider for the dictionaries.
|
||||
/// </summary>
|
||||
#define DICTIONARY_DEFAULT_SLACK_SCALE 3
|
||||
|
||||
/// <summary>
|
||||
/// Function for dictionary that tells how change hash index during iteration (size param is a buckets table size).
|
||||
/// </summary>
|
||||
#define DICTIONARY_PROB_FUNC(size, numChecks) (numChecks)
|
||||
//#define DICTIONARY_PROB_FUNC(size, numChecks) (1)
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
private:
|
||||
State _state;
|
||||
|
||||
void Free()
|
||||
FORCE_INLINE void Free()
|
||||
{
|
||||
if (_state == Occupied)
|
||||
{
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
_state = Empty;
|
||||
}
|
||||
|
||||
void Delete()
|
||||
FORCE_INLINE void Delete()
|
||||
{
|
||||
_state = Deleted;
|
||||
Memory::DestructItem(&Key);
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
void Occupy(const KeyComparableType& key)
|
||||
FORCE_INLINE void Occupy(const KeyComparableType& key)
|
||||
{
|
||||
Memory::ConstructItems(&Key, &key, 1);
|
||||
Memory::ConstructItem(&Value);
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
void Occupy(const KeyComparableType& key, const ValueType& value)
|
||||
FORCE_INLINE void Occupy(const KeyComparableType& key, const ValueType& value)
|
||||
{
|
||||
Memory::ConstructItems(&Key, &key, 1);
|
||||
Memory::ConstructItems(&Value, &value, 1);
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
void Occupy(const KeyComparableType& key, ValueType&& value)
|
||||
FORCE_INLINE void Occupy(const KeyComparableType& key, ValueType&& value)
|
||||
{
|
||||
Memory::ConstructItems(&Key, &key, 1);
|
||||
Memory::MoveItems(&Value, &value, 1);
|
||||
@@ -110,6 +110,33 @@ private:
|
||||
int32 _size = 0;
|
||||
AllocationData _allocation;
|
||||
|
||||
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromSize)
|
||||
{
|
||||
if IF_CONSTEXPR (AllocationType::HasSwap)
|
||||
to.Swap(from);
|
||||
else
|
||||
{
|
||||
to.Allocate(fromSize);
|
||||
Bucket* toData = to.Get();
|
||||
Bucket* fromData = from.Get();
|
||||
for (int32 i = 0; i < fromSize; i++)
|
||||
{
|
||||
Bucket& fromBucket = fromData[i];
|
||||
if (fromBucket.IsOccupied())
|
||||
{
|
||||
Bucket& toBucket = toData[i];
|
||||
Memory::MoveItems(&toBucket.Key, &fromBucket.Key, 1);
|
||||
Memory::MoveItems(&toBucket.Value, &fromBucket.Value, 1);
|
||||
toBucket._state = Bucket::Occupied;
|
||||
Memory::DestructItem(&fromBucket.Key);
|
||||
Memory::DestructItem(&fromBucket.Value);
|
||||
fromBucket._state = Bucket::Empty;
|
||||
}
|
||||
}
|
||||
from.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dictionary"/> class.
|
||||
@@ -132,9 +159,6 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
Dictionary(Dictionary&& other) noexcept
|
||||
: _elementsCount(other._elementsCount)
|
||||
, _deletedCount(other._deletedCount)
|
||||
, _size(other._size)
|
||||
{
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
@@ -142,7 +166,7 @@ public:
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
MoveToEmpty(_allocation, other._allocation, _size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -183,7 +207,7 @@ public:
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
MoveToEmpty(_allocation, other._allocation, _size);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -375,8 +399,12 @@ public:
|
||||
template<typename KeyComparableType>
|
||||
ValueType& At(const KeyComparableType& key)
|
||||
{
|
||||
// Check if need to rehash elements (prevent many deleted elements that use too much of capacity)
|
||||
if (_deletedCount > _size / DICTIONARY_DEFAULT_SLACK_SCALE)
|
||||
Compact();
|
||||
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
EnsureCapacity((_elementsCount + 1) * DICTIONARY_DEFAULT_SLACK_SCALE + _deletedCount);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
@@ -388,9 +416,9 @@ public:
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
_elementsCount++;
|
||||
Bucket& bucket = _allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket.Occupy(key);
|
||||
_elementsCount++;
|
||||
return bucket.Value;
|
||||
}
|
||||
|
||||
@@ -493,7 +521,7 @@ public:
|
||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value)
|
||||
Delete(i->Value);
|
||||
::Delete(i->Value);
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
@@ -509,7 +537,7 @@ public:
|
||||
return;
|
||||
ASSERT(capacity >= 0);
|
||||
AllocationData oldAllocation;
|
||||
oldAllocation.Swap(_allocation);
|
||||
MoveToEmpty(oldAllocation, _allocation, _size);
|
||||
const int32 oldSize = _size;
|
||||
const int32 oldElementsCount = _elementsCount;
|
||||
_deletedCount = _elementsCount = 0;
|
||||
@@ -533,13 +561,22 @@ public:
|
||||
}
|
||||
_size = capacity;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
if (oldElementsCount != 0 && preserveContents)
|
||||
if (oldElementsCount != 0 && capacity != 0 && preserveContents)
|
||||
{
|
||||
// TODO; move keys and values on realloc
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < oldSize; i++)
|
||||
{
|
||||
if (oldData[i].IsOccupied())
|
||||
Add(oldData[i].Key, MoveTemp(oldData[i].Value));
|
||||
Bucket& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.Key, pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Memory::MoveItems(&bucket->Key, &oldBucket.Key, 1);
|
||||
Memory::MoveItems(&bucket->Value, &oldBucket.Value, 1);
|
||||
bucket->_state = Bucket::Occupied;
|
||||
_elementsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldElementsCount != 0)
|
||||
@@ -558,9 +595,9 @@ public:
|
||||
{
|
||||
if (_size >= minCapacity)
|
||||
return;
|
||||
if (minCapacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
minCapacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
if (capacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
capacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
|
||||
@@ -570,10 +607,19 @@ public:
|
||||
/// <param name="other">The other collection.</param>
|
||||
void Swap(Dictionary& other)
|
||||
{
|
||||
::Swap(_elementsCount, other._elementsCount);
|
||||
::Swap(_deletedCount, other._deletedCount);
|
||||
::Swap(_size, other._size);
|
||||
_allocation.Swap(other._allocation);
|
||||
if IF_CONSTEXPR (AllocationType::HasSwap)
|
||||
{
|
||||
::Swap(_elementsCount, other._elementsCount);
|
||||
::Swap(_deletedCount, other._deletedCount);
|
||||
::Swap(_size, other._size);
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
Dictionary tmp = MoveTemp(other);
|
||||
other = *this;
|
||||
*this = MoveTemp(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -584,24 +630,10 @@ public:
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>Weak reference to the stored bucket.</returns>
|
||||
template<typename KeyComparableType>
|
||||
Bucket* Add(const KeyComparableType& key, const ValueType& value)
|
||||
FORCE_INLINE Bucket* Add(const KeyComparableType& key, const ValueType& value)
|
||||
{
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
// Ensure key is unknown
|
||||
ASSERT(pos.ObjectIndex == -1 && "That key has been already added to the dictionary.");
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Bucket* bucket = OnAdd(key);
|
||||
bucket->Occupy(key, value);
|
||||
_elementsCount++;
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
@@ -612,24 +644,10 @@ public:
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>Weak reference to the stored bucket.</returns>
|
||||
template<typename KeyComparableType>
|
||||
Bucket* Add(const KeyComparableType& key, ValueType&& value)
|
||||
FORCE_INLINE Bucket* Add(const KeyComparableType& key, ValueType&& value)
|
||||
{
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
// Ensure key is unknown
|
||||
ASSERT(pos.ObjectIndex == -1 && "That key has been already added to the dictionary.");
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Bucket* bucket = OnAdd(key);
|
||||
bucket->Occupy(key, MoveTemp(value));
|
||||
_elementsCount++;
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
@@ -851,7 +869,7 @@ public:
|
||||
return Iterator(this, _size);
|
||||
}
|
||||
|
||||
protected:
|
||||
private:
|
||||
/// <summary>
|
||||
/// The result container of the dictionary item lookup searching.
|
||||
/// </summary>
|
||||
@@ -911,4 +929,66 @@ protected:
|
||||
result.ObjectIndex = -1;
|
||||
result.FreeSlotIndex = insertPos;
|
||||
}
|
||||
|
||||
template<typename KeyComparableType>
|
||||
Bucket* OnAdd(const KeyComparableType& key)
|
||||
{
|
||||
// Check if need to rehash elements (prevent many deleted elements that use too much of capacity)
|
||||
if (_deletedCount > _size / DICTIONARY_DEFAULT_SLACK_SCALE)
|
||||
Compact();
|
||||
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity((_elementsCount + 1) * DICTIONARY_DEFAULT_SLACK_SCALE + _deletedCount);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
// Ensure key is unknown
|
||||
ASSERT(pos.ObjectIndex == -1 && "That key has been already added to the dictionary.");
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
_elementsCount++;
|
||||
return &_allocation.Get()[pos.FreeSlotIndex];
|
||||
}
|
||||
|
||||
void Compact()
|
||||
{
|
||||
if (_elementsCount == 0)
|
||||
{
|
||||
// Fast path if it's empty
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebuild entire table completely
|
||||
AllocationData oldAllocation;
|
||||
MoveToEmpty(oldAllocation, _allocation, _size);
|
||||
_allocation.Allocate(_size);
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
{
|
||||
Bucket& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.Key, pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Memory::MoveItems(&bucket->Key, &oldBucket.Key, 1);
|
||||
Memory::MoveItems(&bucket->Value, &oldBucket.Value, 1);
|
||||
bucket->_state = Bucket::Occupied;
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
oldData[i].Free();
|
||||
}
|
||||
_deletedCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,26 +37,33 @@ public:
|
||||
private:
|
||||
State _state;
|
||||
|
||||
void Free()
|
||||
FORCE_INLINE void Free()
|
||||
{
|
||||
if (_state == Occupied)
|
||||
Memory::DestructItem(&Item);
|
||||
_state = Empty;
|
||||
}
|
||||
|
||||
void Delete()
|
||||
FORCE_INLINE void Delete()
|
||||
{
|
||||
_state = Deleted;
|
||||
Memory::DestructItem(&Item);
|
||||
}
|
||||
|
||||
template<typename ItemType>
|
||||
void Occupy(const ItemType& item)
|
||||
FORCE_INLINE void Occupy(const ItemType& item)
|
||||
{
|
||||
Memory::ConstructItems(&Item, &item, 1);
|
||||
_state = Occupied;
|
||||
}
|
||||
|
||||
template<typename ItemType>
|
||||
FORCE_INLINE void Occupy(ItemType& item)
|
||||
{
|
||||
Memory::MoveItems(&Item, &item, 1);
|
||||
_state = Occupied;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool IsEmpty() const
|
||||
{
|
||||
return _state == Empty;
|
||||
@@ -86,6 +93,31 @@ private:
|
||||
int32 _size = 0;
|
||||
AllocationData _allocation;
|
||||
|
||||
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromSize)
|
||||
{
|
||||
if IF_CONSTEXPR (AllocationType::HasSwap)
|
||||
to.Swap(from);
|
||||
else
|
||||
{
|
||||
to.Allocate(fromSize);
|
||||
Bucket* toData = to.Get();
|
||||
Bucket* fromData = from.Get();
|
||||
for (int32 i = 0; i < fromSize; i++)
|
||||
{
|
||||
Bucket& fromBucket = fromData[i];
|
||||
if (fromBucket.IsOccupied())
|
||||
{
|
||||
Bucket& toBucket = toData[i];
|
||||
Memory::MoveItems(&toBucket.Item, &fromBucket.Item, 1);
|
||||
toBucket._state = Bucket::Occupied;
|
||||
Memory::DestructItem(&fromBucket.Item);
|
||||
fromBucket._state = Bucket::Empty;
|
||||
}
|
||||
}
|
||||
from.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HashSet"/> class.
|
||||
@@ -108,9 +140,6 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
HashSet(HashSet&& other) noexcept
|
||||
: _elementsCount(other._elementsCount)
|
||||
, _deletedCount(other._deletedCount)
|
||||
, _size(other._size)
|
||||
{
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
@@ -118,7 +147,7 @@ public:
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
MoveToEmpty(_allocation, other._allocation, _size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -159,7 +188,7 @@ public:
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
MoveToEmpty(_allocation, other._allocation, _size);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -169,7 +198,7 @@ public:
|
||||
/// </summary>
|
||||
~HashSet()
|
||||
{
|
||||
SetCapacity(0, false);
|
||||
Clear();
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -216,6 +245,7 @@ public:
|
||||
HashSet* _collection;
|
||||
int32 _index;
|
||||
|
||||
public:
|
||||
Iterator(HashSet* collection, const int32 index)
|
||||
: _collection(collection)
|
||||
, _index(index)
|
||||
@@ -228,7 +258,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Iterator()
|
||||
: _collection(nullptr)
|
||||
, _index(-1)
|
||||
{
|
||||
}
|
||||
|
||||
Iterator(const Iterator& i)
|
||||
: _collection(i._collection)
|
||||
, _index(i._index)
|
||||
@@ -242,6 +277,11 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE int32 Index() const
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool IsEnd() const
|
||||
{
|
||||
return _index == _collection->_size;
|
||||
@@ -374,7 +414,7 @@ public:
|
||||
return;
|
||||
ASSERT(capacity >= 0);
|
||||
AllocationData oldAllocation;
|
||||
oldAllocation.Swap(_allocation);
|
||||
MoveToEmpty(oldAllocation, _allocation, _size);
|
||||
const int32 oldSize = _size;
|
||||
const int32 oldElementsCount = _elementsCount;
|
||||
_deletedCount = _elementsCount = 0;
|
||||
@@ -398,13 +438,21 @@ public:
|
||||
}
|
||||
_size = capacity;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
if (oldElementsCount != 0 && preserveContents)
|
||||
if (oldElementsCount != 0 && capacity != 0 && preserveContents)
|
||||
{
|
||||
// TODO; move keys and values on realloc
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < oldSize; i++)
|
||||
{
|
||||
if (oldData[i].IsOccupied())
|
||||
Add(oldData[i].Item);
|
||||
Bucket& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.Item, pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Memory::MoveItems(&bucket->Item, &oldBucket.Item, 1);
|
||||
bucket->_state = Bucket::Occupied;
|
||||
_elementsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldElementsCount != 0)
|
||||
@@ -421,14 +469,35 @@ public:
|
||||
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
|
||||
void EnsureCapacity(int32 minCapacity, bool preserveContents = true)
|
||||
{
|
||||
if (Capacity() >= minCapacity)
|
||||
if (_size >= minCapacity)
|
||||
return;
|
||||
if (minCapacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
minCapacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
const int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
int32 capacity = _allocation.CalculateCapacityGrow(_size, minCapacity);
|
||||
if (capacity < DICTIONARY_DEFAULT_CAPACITY)
|
||||
capacity = DICTIONARY_DEFAULT_CAPACITY;
|
||||
SetCapacity(capacity, preserveContents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swaps the contents of collection with the other object without copy operation. Performs fast internal data exchange.
|
||||
/// </summary>
|
||||
/// <param name="other">The other collection.</param>
|
||||
void Swap(HashSet& other)
|
||||
{
|
||||
if IF_CONSTEXPR (AllocationType::HasSwap)
|
||||
{
|
||||
::Swap(_elementsCount, other._elementsCount);
|
||||
::Swap(_deletedCount, other._deletedCount);
|
||||
::Swap(_size, other._size);
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
HashSet tmp = MoveTemp(other);
|
||||
other = *this;
|
||||
*this = MoveTemp(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Add element to the collection.
|
||||
@@ -438,24 +507,23 @@ public:
|
||||
template<typename ItemType>
|
||||
bool Add(const ItemType& item)
|
||||
{
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
Bucket* bucket = OnAdd(item);
|
||||
if (bucket)
|
||||
bucket->Occupy(item);
|
||||
return bucket != nullptr;
|
||||
}
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(item, pos);
|
||||
|
||||
// Check if object has been already added
|
||||
if (pos.ObjectIndex != -1)
|
||||
return false;
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket->Occupy(item);
|
||||
_elementsCount++;
|
||||
|
||||
return true;
|
||||
/// <summary>
|
||||
/// Add element to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The element to add to the set.</param>
|
||||
/// <returns>True if element has been added to the collection, otherwise false if the element is already present.</returns>
|
||||
bool Add(T&& item)
|
||||
{
|
||||
Bucket* bucket = OnAdd(item);
|
||||
if (bucket)
|
||||
bucket->Occupy(MoveTemp(item));
|
||||
return bucket != nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -593,7 +661,7 @@ public:
|
||||
return Iterator(this, _size);
|
||||
}
|
||||
|
||||
protected:
|
||||
private:
|
||||
/// <summary>
|
||||
/// The result container of the set item lookup searching.
|
||||
/// </summary>
|
||||
@@ -654,4 +722,66 @@ protected:
|
||||
result.ObjectIndex = -1;
|
||||
result.FreeSlotIndex = insertPos;
|
||||
}
|
||||
|
||||
template<typename ItemType>
|
||||
Bucket* OnAdd(const ItemType& key)
|
||||
{
|
||||
// Check if need to rehash elements (prevent many deleted elements that use too much of capacity)
|
||||
if (_deletedCount > _size / DICTIONARY_DEFAULT_SLACK_SCALE)
|
||||
Compact();
|
||||
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity((_elementsCount + 1) * DICTIONARY_DEFAULT_SLACK_SCALE + _deletedCount);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
FindPosition(key, pos);
|
||||
|
||||
// Check if object has been already added
|
||||
if (pos.ObjectIndex != -1)
|
||||
return nullptr;
|
||||
|
||||
// Insert
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
_elementsCount++;
|
||||
return &_allocation.Get()[pos.FreeSlotIndex];
|
||||
}
|
||||
|
||||
void Compact()
|
||||
{
|
||||
if (_elementsCount == 0)
|
||||
{
|
||||
// Fast path if it's empty
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebuild entire table completely
|
||||
AllocationData oldAllocation;
|
||||
MoveToEmpty(oldAllocation, _allocation, _size);
|
||||
_allocation.Allocate(_size);
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i]._state = Bucket::Empty;
|
||||
Bucket* oldData = oldAllocation.Get();
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
{
|
||||
Bucket& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.Item, pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
|
||||
Memory::MoveItems(&bucket->Item, &oldBucket.Item, 1);
|
||||
bucket->_state = Bucket::Occupied;
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
oldData[i].Free();
|
||||
}
|
||||
_deletedCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,3 +93,10 @@
|
||||
#endif
|
||||
|
||||
#define PACK_STRUCT(__Declaration__) PACK_BEGIN() __Declaration__ PACK_END()
|
||||
|
||||
// C++ 17
|
||||
#if __cplusplus >= 201703L
|
||||
#define IF_CONSTEXPR constexpr
|
||||
#else
|
||||
#define IF_CONSTEXPR
|
||||
#endif
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Config/Settings.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Serialization/SerializationFwd.h"
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the display mode of a game window.
|
||||
|
||||
+101
-114
@@ -226,7 +226,7 @@ public:
|
||||
/// <returns>Function result</returns>
|
||||
FORCE_INLINE ReturnType operator()(Params... params) const
|
||||
{
|
||||
ASSERT(_function);
|
||||
ASSERT_LOW_LAYER(_function);
|
||||
return _function(_callee, Forward<Params>(params)...);
|
||||
}
|
||||
|
||||
@@ -289,8 +289,13 @@ protected:
|
||||
intptr volatile _ptr = 0;
|
||||
intptr volatile _size = 0;
|
||||
#else
|
||||
HashSet<FunctionType>* _functions = nullptr;
|
||||
CriticalSection* _locker = nullptr;
|
||||
struct Data
|
||||
{
|
||||
HashSet<FunctionType> Functions;
|
||||
CriticalSection Locker;
|
||||
};
|
||||
// Holds pointer to Data with Functions and Locker. Thread-safe access via atomic operations.
|
||||
intptr volatile _data = 0;
|
||||
#endif
|
||||
typedef void (*StubSignature)(void*, Params...);
|
||||
|
||||
@@ -314,15 +319,12 @@ public:
|
||||
_ptr = (intptr)newBindings;
|
||||
_size = newSize;
|
||||
#else
|
||||
if (other._functions == nullptr)
|
||||
Data* otherData = (Data*)Platform::AtomicRead(&_data);
|
||||
if (otherData == nullptr)
|
||||
return;
|
||||
_functions = New<HashSet<FunctionType>>(*other._functions);
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Item._function && i->Item._lambda)
|
||||
i->Item.LambdaCtor();
|
||||
}
|
||||
_locker = other._locker;
|
||||
ScopeLock lock(otherData->Locker);
|
||||
for (auto i = otherData->Functions.Begin(); i.IsNotEnd(); ++i)
|
||||
Bind(i->Item);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -334,10 +336,8 @@ public:
|
||||
other._ptr = 0;
|
||||
other._size = 0;
|
||||
#else
|
||||
_functions = other._functions;
|
||||
_locker = other._locker;
|
||||
other._functions = nullptr;
|
||||
other._locker = nullptr;
|
||||
_data = other._data;
|
||||
other._data = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -356,20 +356,11 @@ public:
|
||||
Allocator::Free((void*)_ptr);
|
||||
}
|
||||
#else
|
||||
if (_locker != nullptr)
|
||||
Data* data = (Data*)_data;
|
||||
if (data)
|
||||
{
|
||||
Allocator::Free(_locker);
|
||||
_locker = nullptr;
|
||||
}
|
||||
if (_functions != nullptr)
|
||||
{
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Item._lambda)
|
||||
i->Item.LambdaCtor();
|
||||
}
|
||||
Allocator::Free(_functions);
|
||||
_functions = nullptr;
|
||||
_data = 0;
|
||||
Delete(data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -385,8 +376,13 @@ public:
|
||||
for (intptr i = 0; i < size; i++)
|
||||
Bind(bindings[i]);
|
||||
#else
|
||||
for (auto i = other._functions->Begin(); i.IsNotEnd(); ++i)
|
||||
Bind(i->Item);
|
||||
Data* otherData = (Data*)Platform::AtomicRead(&_data);
|
||||
if (otherData != nullptr)
|
||||
{
|
||||
ScopeLock lock(otherData->Locker);
|
||||
for (auto i = otherData->Functions.Begin(); i.IsNotEnd(); ++i)
|
||||
Bind(i->Item);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
@@ -402,10 +398,8 @@ public:
|
||||
other._ptr = 0;
|
||||
other._size = 0;
|
||||
#else
|
||||
_functions = other._functions;
|
||||
_locker = other._locker;
|
||||
other._functions = nullptr;
|
||||
other._locker = nullptr;
|
||||
_data = other._data;
|
||||
other._data = 0;
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
@@ -507,12 +501,20 @@ public:
|
||||
Allocator::Free(bindings);
|
||||
}
|
||||
#else
|
||||
if (_locker == nullptr)
|
||||
_locker = New<CriticalSection>();
|
||||
ScopeLock lock(*_locker);
|
||||
if (_functions == nullptr)
|
||||
_functions = New<HashSet<FunctionType>>(32);
|
||||
_functions->Add(f);
|
||||
Data* data = (Data*)Platform::AtomicRead(&_data);
|
||||
while (!data)
|
||||
{
|
||||
Data* newData = New<Data>();
|
||||
Data* oldData = (Data*)Platform::InterlockedCompareExchange(&_data, (intptr)newData, (intptr)data);
|
||||
if (oldData != data)
|
||||
{
|
||||
// Other thread already set the new data so free it and try again
|
||||
Delete(newData);
|
||||
}
|
||||
data = (Data*)Platform::AtomicRead(&_data);
|
||||
}
|
||||
ScopeLock lock(data->Locker);
|
||||
data->Functions.Add(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -568,13 +570,22 @@ public:
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (_locker == nullptr)
|
||||
_locker = New<CriticalSection>();
|
||||
ScopeLock lock(*_locker);
|
||||
if (_functions && _functions->Contains(f))
|
||||
return;
|
||||
Data* data = (Data*)Platform::AtomicRead(&_data);
|
||||
if (data)
|
||||
{
|
||||
data->Locker.Lock();
|
||||
if (data->Functions.Contains(f))
|
||||
{
|
||||
data->Locker.Unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Bind(f);
|
||||
#if !DELEGATE_USE_ATOMIC
|
||||
if (data)
|
||||
data->Locker.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -583,18 +594,9 @@ public:
|
||||
template<void(*Method)(Params...)>
|
||||
void Unbind()
|
||||
{
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
FunctionType f;
|
||||
f.template Bind<Method>();
|
||||
Unbind(f);
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return;
|
||||
FunctionType f;
|
||||
f.template Bind<Method>();
|
||||
ScopeLock lock(*_locker);
|
||||
_functions->Remove(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -604,18 +606,9 @@ public:
|
||||
template<class T, void(T::*Method)(Params...)>
|
||||
void Unbind(T* callee)
|
||||
{
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
FunctionType f;
|
||||
f.template Bind<T, Method>(callee);
|
||||
Unbind(f);
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return;
|
||||
FunctionType f;
|
||||
f.template Bind<T, Method>(callee);
|
||||
ScopeLock lock(*_locker);
|
||||
_functions->Remove(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -624,16 +617,8 @@ public:
|
||||
/// <param name="method">The method.</param>
|
||||
void Unbind(Signature method)
|
||||
{
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
FunctionType f(method);
|
||||
Unbind(f);
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return;
|
||||
FunctionType f(method);
|
||||
ScopeLock lock(*_locker);
|
||||
_functions->Remove(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -666,10 +651,11 @@ public:
|
||||
Unbind(f);
|
||||
}
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
Data* data = (Data*)Platform::AtomicRead(&_data);
|
||||
if (!data)
|
||||
return;
|
||||
ScopeLock lock(*_locker);
|
||||
_functions->Remove(f);
|
||||
ScopeLock lock(data->Locker);
|
||||
data->Functions.Remove(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -692,15 +678,11 @@ public:
|
||||
Platform::AtomicStore((intptr volatile*)&bindings[i]._callee, 0);
|
||||
}
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
Data* data = (Data*)Platform::AtomicRead(&_data);
|
||||
if (!data)
|
||||
return;
|
||||
ScopeLock lock(*_locker);
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Item._lambda)
|
||||
i->Item.LambdaDtor();
|
||||
}
|
||||
_functions->Clear();
|
||||
ScopeLock lock(data->Locker);
|
||||
data->Functions.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -710,22 +692,24 @@ public:
|
||||
/// <returns>The bound functions count.</returns>
|
||||
int32 Count() const
|
||||
{
|
||||
int32 result = 0;
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
int32 count = 0;
|
||||
const intptr size = Platform::AtomicRead((intptr volatile*)&_size);
|
||||
FunctionType* bindings = (FunctionType*)Platform::AtomicRead((intptr volatile*)&_ptr);
|
||||
for (intptr i = 0; i < size; i++)
|
||||
{
|
||||
if (Platform::AtomicRead((intptr volatile*)&bindings[i]._function) != 0)
|
||||
count++;
|
||||
result++;
|
||||
}
|
||||
return count;
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return 0;
|
||||
ScopeLock lock(*_locker);
|
||||
return _functions->Count();
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (data)
|
||||
{
|
||||
ScopeLock lock(data->Locker);
|
||||
result = data->Functions.Count();
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -736,10 +720,14 @@ public:
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
return (int32)Platform::AtomicRead((intptr volatile*)&_size);
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return 0;
|
||||
ScopeLock lock(*_locker);
|
||||
return _functions->Capacity();
|
||||
int32 result = 0;
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (data)
|
||||
{
|
||||
ScopeLock lock(data->Locker);
|
||||
result = data->Functions.Capacity();
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -759,10 +747,14 @@ public:
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return false;
|
||||
ScopeLock lock(*_locker);
|
||||
return _functions->Count() > 0;
|
||||
bool result = false;
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (data)
|
||||
{
|
||||
ScopeLock lock(data->Locker);
|
||||
result = data->Functions.Count() != 0;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -791,18 +783,13 @@ public:
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return 0;
|
||||
ScopeLock lock(*_locker);
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (data)
|
||||
{
|
||||
if (i->Item._function != nullptr)
|
||||
ScopeLock lock(data->Locker);
|
||||
for (auto i = data->Functions.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
buffer[count]._function = (StubSignature)i->Item._function;
|
||||
buffer[count]._callee = (void*)i->Item._callee;
|
||||
buffer[count]._lambda = (typename FunctionType::Lambda*)i->Item._lambda;
|
||||
if (buffer[count]._lambda)
|
||||
buffer[count].LambdaCtor();
|
||||
new(buffer + count) FunctionType((const FunctionType&)i->Item);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -828,15 +815,15 @@ public:
|
||||
++bindings;
|
||||
}
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
|
||||
if (!data)
|
||||
return;
|
||||
ScopeLock lock(*_locker);
|
||||
for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
|
||||
ScopeLock lock(data->Locker);
|
||||
for (auto i = data->Functions.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto function = (StubSignature)(i->Item._function);
|
||||
auto callee = (void*)(i->Item._callee);
|
||||
if (function != nullptr)
|
||||
function(callee, Forward<Params>(params)...);
|
||||
const FunctionType& item = i->Item;
|
||||
ASSERT_LOW_LAYER(item._function);
|
||||
item._function(item._callee, Forward<Params>(params)...);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -728,9 +728,7 @@ namespace Math
|
||||
/// <summary>
|
||||
/// Returns value based on comparand. The main purpose of this function is to avoid branching based on floating point comparison which can be avoided via compiler intrinsics.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Please note that this doesn't define what happens in the case of NaNs as there might be platform specific differences.
|
||||
/// </remarks>
|
||||
/// <remarks>Please note that this doesn't define what happens in the case of NaNs as there might be platform specific differences.</remarks>
|
||||
/// <param name="comparand">Comparand the results are based on.</param>
|
||||
/// <param name="valueGEZero">The result value if comparand >= 0.</param>
|
||||
/// <param name="valueLTZero">The result value if comparand < 0.</param>
|
||||
@@ -891,6 +889,18 @@ namespace Math
|
||||
return Lerp<T>(a, b, alpha < 0.5f ? InterpCircularIn(0.f, 1.f, alpha * 2.f) * 0.5f : InterpCircularOut(0.f, 1.f, alpha * 2.f - 1.f) * 0.5f + 0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping pongs the value <paramref name="t"/>, so that it is never larger than <paramref name="length"/> and never smaller than 0.
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
template<class T>
|
||||
static FORCE_INLINE T PingPong(const T& t, T length)
|
||||
{
|
||||
return length - Abs(Repeat(t, length * 2.0f) - length);
|
||||
}
|
||||
|
||||
// Rotates position about the given axis by the given angle, in radians, and returns the offset to position
|
||||
Vector3 FLAXENGINE_API RotateAboutAxis(const Vector3& normalizedRotationAxis, float angle, const Vector3& positionOnAxis, const Vector3& position);
|
||||
|
||||
|
||||
@@ -382,9 +382,8 @@ void Quaternion::GetRotationFromTo(const Float3& from, const Float3& to, Quatern
|
||||
v0.Normalize();
|
||||
v1.Normalize();
|
||||
|
||||
const float d = Float3::Dot(v0, v1);
|
||||
|
||||
// If dot == 1, vectors are the same
|
||||
const float d = Float3::Dot(v0, v1);
|
||||
if (d >= 1.0f)
|
||||
{
|
||||
result = Identity;
|
||||
|
||||
@@ -1077,6 +1077,115 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shortest arc quaternion to rotate this vector to the destination vector.
|
||||
/// </summary>
|
||||
/// <param name="from">The source vector.</param>
|
||||
/// <param name="to">The destination vector.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <param name="fallbackAxis">The fallback axis.</param>
|
||||
public static void GetRotationFromTo(ref Float3 from, ref Float3 to, out Quaternion result, ref Float3 fallbackAxis)
|
||||
{
|
||||
// Based on Stan Melax's article in Game Programming Gems
|
||||
|
||||
Float3 v0 = from;
|
||||
Float3 v1 = to;
|
||||
v0.Normalize();
|
||||
v1.Normalize();
|
||||
|
||||
// If dot == 1, vectors are the same
|
||||
float d = Float3.Dot(ref v0, ref v1);
|
||||
if (d >= 1.0f)
|
||||
{
|
||||
result = Identity;
|
||||
return;
|
||||
}
|
||||
|
||||
if (d < 1e-6f - 1.0f)
|
||||
{
|
||||
if (fallbackAxis != Float3.Zero)
|
||||
{
|
||||
// Rotate 180 degrees about the fallback axis
|
||||
RotationAxis(ref fallbackAxis, Mathf.Pi, out result);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate an axis
|
||||
Float3 axis = Float3.Cross(Float3.UnitX, from);
|
||||
if (axis.LengthSquared < Mathf.Epsilon) // Pick another if colinear
|
||||
axis = Float3.Cross(Float3.UnitY, from);
|
||||
axis.Normalize();
|
||||
RotationAxis(ref axis, Mathf.Pi, out result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float s = Mathf.Sqrt((1 + d) * 2);
|
||||
float invS = 1 / s;
|
||||
Float3.Cross(ref v0, ref v1, out var c);
|
||||
result.X = c.X * invS;
|
||||
result.Y = c.Y * invS;
|
||||
result.Z = c.Z * invS;
|
||||
result.W = s * 0.5f;
|
||||
result.Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shortest arc quaternion to rotate this vector to the destination vector.
|
||||
/// </summary>
|
||||
/// <param name="from">The source vector.</param>
|
||||
/// <param name="to">The destination vector.</param>
|
||||
/// <param name="fallbackAxis">The fallback axis.</param>
|
||||
/// <returns>The rotation.</returns>
|
||||
public static Quaternion GetRotationFromTo(Float3 from, Float3 to, Float3 fallbackAxis)
|
||||
{
|
||||
GetRotationFromTo(ref from, ref to, out var result, ref fallbackAxis);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized.
|
||||
/// </summary>
|
||||
/// <param name="from">The source vector.</param>
|
||||
/// <param name="to">The destination vector.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
public static void FindBetween(ref Float3 from, ref Float3 to, out Quaternion result)
|
||||
{
|
||||
// http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
|
||||
float normFromNormTo = Mathf.Sqrt(from.LengthSquared * to.LengthSquared);
|
||||
if (normFromNormTo < Mathf.Epsilon)
|
||||
{
|
||||
result = Identity;
|
||||
return;
|
||||
}
|
||||
float w = normFromNormTo + Float3.Dot(from, to);
|
||||
if (w < 1.0-6f * normFromNormTo)
|
||||
{
|
||||
result = Mathf.Abs(from.X) > Mathf.Abs(from.Z)
|
||||
? new Quaternion(-from.Y, from.X, 0.0f, 0.0f)
|
||||
: new Quaternion(0.0f, -from.Z, from.Y, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Float3 cross = Float3.Cross(from, to);
|
||||
result = new Quaternion(cross.X, cross.Y, cross.Z, w);
|
||||
}
|
||||
result.Normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized.
|
||||
/// </summary>
|
||||
/// <param name="from">The source vector.</param>
|
||||
/// <param name="to">The destination vector.</param>
|
||||
/// <returns>The rotation.</returns>
|
||||
public static Quaternion FindBetween(Float3 from, Float3 to)
|
||||
{
|
||||
FindBetween(ref from, ref to, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a left-handed spherical billboard that rotates around a specified object position.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,9 +13,7 @@ namespace FlaxEngine.TypeConverters
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
@@ -23,9 +21,7 @@ namespace FlaxEngine.TypeConverters
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Double2Converter : TypeConverter
|
||||
internal class Double2Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Double2(double.Parse(v[0], culture), double.Parse(v[1], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Double3Converter : TypeConverter
|
||||
internal class Double3Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Double3(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Double4Converter : TypeConverter
|
||||
internal class Double4Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Double4(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture), double.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Float2Converter : TypeConverter
|
||||
internal class Float2Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Float2(float.Parse(v[0], culture), float.Parse(v[1], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Float3Converter : TypeConverter
|
||||
internal class Float3Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Float3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,15 +7,13 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Float4Converter : TypeConverter
|
||||
internal class VectorConverter : TypeConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
@@ -23,18 +21,32 @@ namespace FlaxEngine.TypeConverters
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
internal static string[] GetParts(string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
if (v.Length == 1)
|
||||
{
|
||||
// When converting from ToString()
|
||||
v = str.Split(' ');
|
||||
for (int i = 0; i < v.Length; i++)
|
||||
v[i] = v[i].Substring(v[i].IndexOf(':') + 1);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Float4Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Float4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Int2Converter : TypeConverter
|
||||
internal class Int2Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Int2(int.Parse(v[0], culture), int.Parse(v[1], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Int3Converter : TypeConverter
|
||||
internal class Int3Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Int3(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Int4Converter : TypeConverter
|
||||
internal class Int4Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Int4(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture), int.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class QuaternionConverter : TypeConverter
|
||||
internal class QuaternionConverter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Quaternion(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Vector2Converter : TypeConverter
|
||||
internal class Vector2Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Vector2(float.Parse(v[0], culture), float.Parse(v[1], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Vector3Converter : TypeConverter
|
||||
internal class Vector3Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Vector3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Vector4Converter : TypeConverter
|
||||
internal class Vector4Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Vector4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -646,6 +646,12 @@ inline Vector2Base<T> operator/(typename TOtherFloat<T>::Type a, const Vector2Ba
|
||||
return Vector2Base<T>(a) / b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline uint32 GetHash(const Vector2Base<T>& key)
|
||||
{
|
||||
return (*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y;
|
||||
}
|
||||
|
||||
namespace Math
|
||||
{
|
||||
template<typename T>
|
||||
|
||||
@@ -977,6 +977,12 @@ inline Vector3Base<T> operator/(typename TOtherFloat<T>::Type a, const Vector3Ba
|
||||
return Vector3Base<T>(a) / b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline uint32 GetHash(const Vector3Base<T>& key)
|
||||
{
|
||||
return (((*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y) * 397) ^ *(uint32*)&key.Z;
|
||||
}
|
||||
|
||||
namespace Math
|
||||
{
|
||||
template<typename T>
|
||||
|
||||
@@ -552,6 +552,12 @@ inline Vector4Base<T> operator/(typename TOtherFloat<T>::Type a, const Vector4Ba
|
||||
return Vector4Base<T>(a) / b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline uint32 GetHash(const Vector4Base<T>& key)
|
||||
{
|
||||
return (((((*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y) * 397) ^ *(uint32*)&key.Z) * 397) ^*(uint32*)&key.W;
|
||||
}
|
||||
|
||||
namespace Math
|
||||
{
|
||||
template<typename T>
|
||||
|
||||
@@ -12,6 +12,8 @@ template<int Capacity>
|
||||
class FixedAllocation
|
||||
{
|
||||
public:
|
||||
enum { HasSwap = false };
|
||||
|
||||
template<typename T>
|
||||
class Data
|
||||
{
|
||||
@@ -43,14 +45,14 @@ public:
|
||||
return Capacity;
|
||||
}
|
||||
|
||||
FORCE_INLINE void Allocate(uint64 capacity)
|
||||
FORCE_INLINE void Allocate(int32 capacity)
|
||||
{
|
||||
#if ENABLE_ASSERTION_LOW_LAYERS
|
||||
ASSERT(capacity <= Capacity);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE void Relocate(uint64 capacity, int32 oldCount, int32 newCount)
|
||||
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
|
||||
{
|
||||
#if ENABLE_ASSERTION_LOW_LAYERS
|
||||
ASSERT(capacity <= Capacity);
|
||||
@@ -61,12 +63,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
FORCE_INLINE void Swap(Data& other)
|
||||
void Swap(Data& other)
|
||||
{
|
||||
byte tmp[Capacity * sizeof(T)];
|
||||
Platform::MemoryCopy(tmp, _data, Capacity * sizeof(T));
|
||||
Platform::MemoryCopy(_data, other._data, Capacity * sizeof(T));
|
||||
Platform::MemoryCopy(other._data, tmp, Capacity * sizeof(T));
|
||||
// Not supported
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -77,6 +76,8 @@ public:
|
||||
class HeapAllocation
|
||||
{
|
||||
public:
|
||||
enum { HasSwap = true };
|
||||
|
||||
template<typename T>
|
||||
class Data
|
||||
{
|
||||
@@ -120,12 +121,15 @@ public:
|
||||
capacity |= capacity >> 4;
|
||||
capacity |= capacity >> 8;
|
||||
capacity |= capacity >> 16;
|
||||
capacity = (capacity + 1) * 2;
|
||||
uint64 capacity64 = (uint64)(capacity + 1) * 2;
|
||||
if (capacity64 > MAX_int32)
|
||||
capacity64 = MAX_int32;
|
||||
capacity = (int32)capacity64;
|
||||
}
|
||||
return capacity;
|
||||
}
|
||||
|
||||
FORCE_INLINE void Allocate(uint64 capacity)
|
||||
FORCE_INLINE void Allocate(int32 capacity)
|
||||
{
|
||||
#if ENABLE_ASSERTION_LOW_LAYERS
|
||||
ASSERT(!_data);
|
||||
@@ -137,7 +141,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE void Relocate(uint64 capacity, int32 oldCount, int32 newCount)
|
||||
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
|
||||
{
|
||||
T* newData = capacity != 0 ? (T*)Allocator::Allocate(capacity * sizeof(T)) : nullptr;
|
||||
#if !BUILD_RELEASE
|
||||
@@ -176,6 +180,8 @@ template<int Capacity, typename OtherAllocator = HeapAllocation>
|
||||
class InlinedAllocation
|
||||
{
|
||||
public:
|
||||
enum { HasSwap = false };
|
||||
|
||||
template<typename T>
|
||||
class Data
|
||||
{
|
||||
@@ -210,7 +216,7 @@ public:
|
||||
return minCapacity <= Capacity ? Capacity : _other.CalculateCapacityGrow(capacity, minCapacity);
|
||||
}
|
||||
|
||||
FORCE_INLINE void Allocate(uint64 capacity)
|
||||
FORCE_INLINE void Allocate(int32 capacity)
|
||||
{
|
||||
if (capacity > Capacity)
|
||||
{
|
||||
@@ -219,7 +225,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void Relocate(uint64 capacity, int32 oldCount, int32 newCount)
|
||||
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
|
||||
{
|
||||
// Check if the new allocation will fit into inlined storage
|
||||
if (capacity <= Capacity)
|
||||
@@ -264,14 +270,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void Swap(Data& other)
|
||||
void Swap(Data& other)
|
||||
{
|
||||
byte tmp[Capacity * sizeof(T)];
|
||||
Platform::MemoryCopy(tmp, _data, Capacity * sizeof(T));
|
||||
Platform::MemoryCopy(_data, other._data, Capacity * sizeof(T));
|
||||
Platform::MemoryCopy(other._data, tmp, Capacity * sizeof(T));
|
||||
::Swap(_useOther, other._useOther);
|
||||
_other.Swap(other._other);
|
||||
// Not supported
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Collections/Dictionary.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "String.h"
|
||||
#include "StringView.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
|
||||
/// <summary>
|
||||
@@ -138,6 +139,11 @@ public:
|
||||
_data.Add(*str, str.Length());
|
||||
return *this;
|
||||
}
|
||||
StringBuilder& Append(const StringView& str)
|
||||
{
|
||||
_data.Add(*str, str.Length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Append int to the string
|
||||
// @param val Value to append
|
||||
|
||||
@@ -327,6 +327,18 @@ public:
|
||||
bool operator!=(const String& other) const;
|
||||
|
||||
public:
|
||||
using StringViewBase::StartsWith;
|
||||
FORCE_INLINE bool StartsWith(const StringView& prefix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
|
||||
{
|
||||
return StringViewBase::StartsWith(prefix, searchCase);
|
||||
}
|
||||
|
||||
using StringViewBase::EndsWith;
|
||||
FORCE_INLINE bool EndsWith(const StringView& suffix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
|
||||
{
|
||||
return StringViewBase::EndsWith(suffix, searchCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the left most given number of characters.
|
||||
/// </summary>
|
||||
@@ -511,6 +523,18 @@ public:
|
||||
bool operator!=(const StringAnsi& other) const;
|
||||
|
||||
public:
|
||||
using StringViewBase::StartsWith;
|
||||
FORCE_INLINE bool StartsWith(const StringAnsiView& prefix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
|
||||
{
|
||||
return StringViewBase::StartsWith(prefix, searchCase);
|
||||
}
|
||||
|
||||
using StringViewBase::EndsWith;
|
||||
FORCE_INLINE bool EndsWith(const StringAnsiView& suffix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const
|
||||
{
|
||||
return StringViewBase::EndsWith(suffix, searchCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves substring created from characters starting from startIndex to the String end.
|
||||
/// </summary>
|
||||
|
||||
@@ -2821,7 +2821,10 @@ void Variant::Inline()
|
||||
type = VariantType::Types::Vector4;
|
||||
}
|
||||
if (type != VariantType::Null)
|
||||
{
|
||||
ASSERT(sizeof(data) >= AsBlob.Length);
|
||||
Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length);
|
||||
}
|
||||
}
|
||||
if (type != VariantType::Null)
|
||||
{
|
||||
@@ -2912,6 +2915,60 @@ void Variant::Inline()
|
||||
}
|
||||
}
|
||||
|
||||
void Variant::InvertInline()
|
||||
{
|
||||
byte data[sizeof(Matrix)];
|
||||
switch (Type.Type)
|
||||
{
|
||||
case VariantType::Bool:
|
||||
case VariantType::Int:
|
||||
case VariantType::Uint:
|
||||
case VariantType::Int64:
|
||||
case VariantType::Uint64:
|
||||
case VariantType::Float:
|
||||
case VariantType::Double:
|
||||
case VariantType::Pointer:
|
||||
case VariantType::String:
|
||||
case VariantType::Float2:
|
||||
case VariantType::Float3:
|
||||
case VariantType::Float4:
|
||||
case VariantType::Color:
|
||||
#if !USE_LARGE_WORLDS
|
||||
case VariantType::BoundingSphere:
|
||||
case VariantType::BoundingBox:
|
||||
case VariantType::Ray:
|
||||
#endif
|
||||
case VariantType::Guid:
|
||||
case VariantType::Quaternion:
|
||||
case VariantType::Rectangle:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Int16:
|
||||
case VariantType::Uint16:
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
static_assert(sizeof(data) >= sizeof(AsData), "Invalid memory size.");
|
||||
Platform::MemoryCopy(data, AsData, sizeof(AsData));
|
||||
break;
|
||||
#if USE_LARGE_WORLDS
|
||||
case VariantType::BoundingSphere:
|
||||
case VariantType::BoundingBox:
|
||||
case VariantType::Ray:
|
||||
#endif
|
||||
case VariantType::Transform:
|
||||
case VariantType::Matrix:
|
||||
ASSERT(sizeof(data) >= AsBlob.Length);
|
||||
Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length);
|
||||
break;
|
||||
default:
|
||||
return; // Not used
|
||||
}
|
||||
SetType(VariantType(VariantType::Structure, InBuiltTypesTypeNames[Type.Type]));
|
||||
CopyStructure(data);
|
||||
}
|
||||
|
||||
Variant Variant::NewValue(const StringAnsiView& typeName)
|
||||
{
|
||||
Variant v;
|
||||
|
||||
@@ -372,6 +372,9 @@ public:
|
||||
// Inlines potential value type into in-built format (eg. Vector3 stored as Structure, or String stored as ManagedObject).
|
||||
void Inline();
|
||||
|
||||
// Inverts the inlined value from in-built format into generic storage (eg. Float3 from inlined format into Structure).
|
||||
void InvertInline();
|
||||
|
||||
// Allocates the Variant of the specific type (eg. structure or object or value).
|
||||
static Variant NewValue(const StringAnsiView& typeName);
|
||||
|
||||
|
||||
@@ -7,15 +7,15 @@ Version::Version(int32 major, int32 minor, int32 build, int32 revision)
|
||||
{
|
||||
_major = Math::Max(major, 0);
|
||||
_minor = Math::Max(minor, 0);
|
||||
_build = Math::Max(build, 0);
|
||||
_revision = Math::Max(revision, 0);
|
||||
_build = Math::Max(build, -1);
|
||||
_revision = Math::Max(revision, -1);
|
||||
}
|
||||
|
||||
Version::Version(int32 major, int32 minor, int32 build)
|
||||
{
|
||||
_major = Math::Max(major, 0);
|
||||
_minor = Math::Max(minor, 0);
|
||||
_build = Math::Max(build, 0);
|
||||
_build = Math::Max(build, -1);
|
||||
_revision = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,10 +51,14 @@ namespace Utilities
|
||||
int32 i = 0;
|
||||
double dblSUnits = static_cast<double>(units);
|
||||
for (; static_cast<uint64>(units / static_cast<double>(divider)) > 0; i++, units /= divider)
|
||||
dblSUnits = units / static_cast<double>(divider);
|
||||
dblSUnits = (double)units / (double)divider;
|
||||
if (i >= sizes.Length())
|
||||
i = 0;
|
||||
return String::Format(TEXT("{0} {1}"), RoundTo2DecimalPlaces(dblSUnits), sizes[i]);
|
||||
String text = String::Format(TEXT("{}"), RoundTo2DecimalPlaces(dblSUnits));
|
||||
const int32 dot = text.FindLast('.');
|
||||
if (dot != -1)
|
||||
text = text.Left(dot + 3);
|
||||
return String::Format(TEXT("{0} {1}"), text, sizes[i]);
|
||||
}
|
||||
|
||||
// Converts size of the file (in bytes) to the best fitting string
|
||||
|
||||
@@ -696,12 +696,15 @@ void* DebugDraw::AllocateContext()
|
||||
|
||||
void DebugDraw::FreeContext(void* context)
|
||||
{
|
||||
ASSERT(context);
|
||||
Memory::DestructItem((DebugDrawContext*)context);
|
||||
Allocator::Free(context);
|
||||
}
|
||||
|
||||
void DebugDraw::UpdateContext(void* context, float deltaTime)
|
||||
{
|
||||
if (!context)
|
||||
context = &GlobalContext;
|
||||
((DebugDrawContext*)context)->DebugDrawDefault.Update(deltaTime);
|
||||
((DebugDrawContext*)context)->DebugDrawDepthTest.Update(deltaTime);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "Engine/Threading/MainThreadTask.h"
|
||||
#include "Engine/Threading/ThreadRegistry.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
@@ -327,14 +326,6 @@ void Engine::OnUpdate()
|
||||
|
||||
// Update services
|
||||
EngineService::OnUpdate();
|
||||
|
||||
#ifdef USE_NETCORE
|
||||
// Force GC to run in background periodically to avoid large blocking collections causing hitches
|
||||
if (Time::Update.TicksCount % 60 == 0)
|
||||
{
|
||||
MCore::GC::Collect(MCore::GC::MaxGeneration(), MGCCollectionMode::Forced, false, false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Engine::OnLateUpdate()
|
||||
@@ -531,7 +522,13 @@ void EngineImpl::InitLog()
|
||||
LOG(Info, "Compiled for Dev Environment");
|
||||
#endif
|
||||
LOG(Info, "Version " FLAXENGINE_VERSION_TEXT);
|
||||
LOG(Info, "Compiled: {0} {1}", TEXT(__DATE__), TEXT(__TIME__));
|
||||
const Char* cpp = TEXT("?");
|
||||
if (__cplusplus == 202101L) cpp = TEXT("C++23");
|
||||
else if (__cplusplus == 202002L) cpp = TEXT("C++20");
|
||||
else if (__cplusplus == 201703L) cpp = TEXT("C++17");
|
||||
else if (__cplusplus == 201402L) cpp = TEXT("C++14");
|
||||
else if (__cplusplus == 201103L) cpp = TEXT("C++11");
|
||||
LOG(Info, "Compiled: {0} {1} {2}", TEXT(__DATE__), TEXT(__TIME__), cpp);
|
||||
#ifdef _MSC_VER
|
||||
const String mcsVer = StringUtils::ToString(_MSC_FULL_VER);
|
||||
LOG(Info, "Compiled with Visual C++ {0}.{1}.{2}.{3:0^2d}", mcsVer.Substring(0, 2), mcsVer.Substring(2, 2), mcsVer.Substring(4, 5), _MSC_BUILD);
|
||||
|
||||
@@ -1022,6 +1022,8 @@ namespace FlaxEngine.Interop
|
||||
pair.Value.Free();
|
||||
classAttributesCacheCollectible.Clear();
|
||||
|
||||
FlaxEngine.Json.JsonSerializer.ResetCache();
|
||||
|
||||
// Unload the ALC
|
||||
bool unloading = true;
|
||||
scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; };
|
||||
@@ -1269,6 +1271,9 @@ namespace FlaxEngine.Interop
|
||||
case Type _ when type == typeof(IntPtr):
|
||||
monoType = MTypes.Ptr;
|
||||
break;
|
||||
case Type _ when type.IsPointer:
|
||||
monoType = MTypes.Ptr;
|
||||
break;
|
||||
case Type _ when type.IsEnum:
|
||||
monoType = MTypes.Enum;
|
||||
break;
|
||||
|
||||
@@ -278,44 +278,70 @@ namespace FlaxEngine.Interop
|
||||
if (typeCache.TryGetValue(typeName, out Type type))
|
||||
return type;
|
||||
|
||||
type = Type.GetType(typeName, ResolveAssemblyByName, null);
|
||||
type = Type.GetType(typeName, ResolveAssembly, null);
|
||||
if (type == null)
|
||||
{
|
||||
foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
{
|
||||
type = assembly.GetType(typeName);
|
||||
if (type != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
type = ResolveSlow(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
string oldTypeName = typeName;
|
||||
string fullTypeName = typeName;
|
||||
typeName = typeName.Substring(0, typeName.IndexOf(','));
|
||||
type = Type.GetType(typeName, ResolveAssemblyByName, null);
|
||||
type = Type.GetType(typeName, ResolveAssembly, null);
|
||||
if (type == null)
|
||||
{
|
||||
foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
{
|
||||
type = assembly.GetType(typeName);
|
||||
if (type != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
typeName = oldTypeName;
|
||||
type = ResolveSlow(typeName);
|
||||
|
||||
typeName = fullTypeName;
|
||||
}
|
||||
|
||||
typeCache.Add(typeName, type);
|
||||
|
||||
return type;
|
||||
|
||||
static Type ResolveSlow(string typeName)
|
||||
{
|
||||
foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
{
|
||||
var type = assembly.GetType(typeName);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Assembly ResolveAssembly(AssemblyName name) => ResolveScriptingAssemblyByName(name, allowPartial: false);
|
||||
}
|
||||
|
||||
private static Assembly ResolveAssemblyByName(AssemblyName assemblyName)
|
||||
/// <summary>Find <paramref name="assemblyName"/> among the scripting assemblies.</summary>
|
||||
/// <param name="assemblyName">The name to find</param>
|
||||
/// <param name="allowPartial">If true, partial names should be allowed to be resolved.</param>
|
||||
/// <returns>The resolved assembly, or null if none could be found.</returns>
|
||||
internal static Assembly ResolveScriptingAssemblyByName(AssemblyName assemblyName, bool allowPartial = false)
|
||||
{
|
||||
foreach (Assembly assembly in scriptingAssemblyLoadContext.Assemblies)
|
||||
if (assembly.GetName() == assemblyName)
|
||||
var lc = scriptingAssemblyLoadContext;
|
||||
|
||||
if (lc is null)
|
||||
return null;
|
||||
|
||||
foreach (Assembly assembly in lc.Assemblies)
|
||||
{
|
||||
var curName = assembly.GetName();
|
||||
|
||||
if (curName == assemblyName)
|
||||
return assembly;
|
||||
}
|
||||
|
||||
if (allowPartial) // Check partial names if full name isn't found
|
||||
{
|
||||
string partialName = assemblyName.Name;
|
||||
|
||||
foreach (Assembly assembly in lc.Assemblies)
|
||||
{
|
||||
var curName = assembly.GetName();
|
||||
|
||||
if (curName.Name == partialName)
|
||||
return assembly;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1109,7 +1135,7 @@ namespace FlaxEngine.Interop
|
||||
marshallers[i](fields[i], offsets[i], ref managedValue, fieldPtr, out int fieldSize);
|
||||
fieldPtr += fieldSize;
|
||||
}
|
||||
Assert.IsTrue((fieldPtr - nativePtr) <= Unsafe.SizeOf<T>());
|
||||
//Assert.IsTrue((fieldPtr - nativePtr) <= GetTypeSize(typeof(T)));
|
||||
}
|
||||
|
||||
internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef)
|
||||
@@ -1156,7 +1182,7 @@ namespace FlaxEngine.Interop
|
||||
marshallers[i](fields[i], offsets[i], ref managedValue, nativePtr, out int fieldSize);
|
||||
nativePtr += fieldSize;
|
||||
}
|
||||
Assert.IsTrue((nativePtr - fieldPtr) <= Unsafe.SizeOf<T>());
|
||||
//Assert.IsTrue((nativePtr - fieldPtr) <= GetTypeSize(typeof(T)));
|
||||
}
|
||||
|
||||
internal static void ToNative(ref T managedValue, IntPtr nativePtr)
|
||||
@@ -1302,7 +1328,8 @@ namespace FlaxEngine.Interop
|
||||
#if !USE_AOT
|
||||
internal bool TryGetDelegate(out Invoker.MarshalAndInvokeDelegate outDeleg, out object outDelegInvoke)
|
||||
{
|
||||
if (invokeDelegate == null)
|
||||
// Skip using in-built delegate for value types (eg. Transform) to properly handle instance value passing to method
|
||||
if (invokeDelegate == null && !method.DeclaringType.IsValueType)
|
||||
{
|
||||
List<Type> methodTypes = new List<Type>();
|
||||
if (!method.IsStatic)
|
||||
@@ -1553,7 +1580,7 @@ namespace FlaxEngine.Interop
|
||||
private static IntPtr PinValue<T>(T value) where T : struct
|
||||
{
|
||||
// Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
|
||||
int size = Unsafe.SizeOf<T>();
|
||||
int size = GetTypeSize(typeof(T));
|
||||
uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
|
||||
ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
|
||||
if (alloc.size < size)
|
||||
|
||||
@@ -46,7 +46,7 @@ API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API FoliageType : public ScriptingOb
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(FoliageType);
|
||||
friend Foliage;
|
||||
private:
|
||||
int8 _isReady : 1;
|
||||
uint8 _isReady : 1;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -26,12 +26,12 @@ public:
|
||||
, _srcResource(src)
|
||||
, _dstResource(dst)
|
||||
{
|
||||
_srcResource.OnUnload.Bind<GPUCopyResourceTask, &GPUCopyResourceTask::OnResourceUnload>(this);
|
||||
_dstResource.OnUnload.Bind<GPUCopyResourceTask, &GPUCopyResourceTask::OnResourceUnload>(this);
|
||||
_srcResource.Released.Bind<GPUCopyResourceTask, &GPUCopyResourceTask::OnResourceReleased>(this);
|
||||
_dstResource.Released.Bind<GPUCopyResourceTask, &GPUCopyResourceTask::OnResourceReleased>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResourceUnload(GPUResourceReference* ref)
|
||||
void OnResourceReleased()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
@@ -47,14 +47,11 @@ protected:
|
||||
// [GPUTask]
|
||||
Result run(GPUTasksContext* context) override
|
||||
{
|
||||
if (_srcResource.IsMissing() || _dstResource.IsMissing())
|
||||
if (!_srcResource || !_dstResource)
|
||||
return Result::MissingResources;
|
||||
|
||||
context->GPU->CopyResource(_dstResource, _srcResource);
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
void OnEnd() override
|
||||
{
|
||||
_srcResource.Unlink();
|
||||
|
||||
@@ -31,12 +31,12 @@ public:
|
||||
, _srcSubresource(srcSubresource)
|
||||
, _dstSubresource(dstSubresource)
|
||||
{
|
||||
_srcResource.OnUnload.Bind<GPUCopySubresourceTask, &GPUCopySubresourceTask::OnResourceUnload>(this);
|
||||
_dstResource.OnUnload.Bind<GPUCopySubresourceTask, &GPUCopySubresourceTask::OnResourceUnload>(this);
|
||||
_srcResource.Released.Bind<GPUCopySubresourceTask, &GPUCopySubresourceTask::OnResourceReleased>(this);
|
||||
_dstResource.Released.Bind<GPUCopySubresourceTask, &GPUCopySubresourceTask::OnResourceReleased>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResourceUnload(GPUResourceReference* ref)
|
||||
void OnResourceReleased()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
@@ -52,14 +52,11 @@ protected:
|
||||
// [GPUTask]
|
||||
Result run(GPUTasksContext* context) override
|
||||
{
|
||||
if (_srcResource.IsMissing() || _dstResource.IsMissing())
|
||||
if (!_srcResource || !_dstResource)
|
||||
return Result::MissingResources;
|
||||
|
||||
context->GPU->CopySubresource(_dstResource, _dstSubresource, _srcResource, _srcSubresource);
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
void OnEnd() override
|
||||
{
|
||||
_srcResource.Unlink();
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
, _buffer(buffer)
|
||||
, _offset(offset)
|
||||
{
|
||||
_buffer.OnUnload.Bind<GPUUploadBufferTask, &GPUUploadBufferTask::OnResourceUnload>(this);
|
||||
_buffer.Released.Bind<GPUUploadBufferTask, &GPUUploadBufferTask::OnResourceReleased>(this);
|
||||
|
||||
if (copyData)
|
||||
_data.Copy(data);
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResourceUnload(BufferReference* ref)
|
||||
void OnResourceReleased()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
@@ -56,14 +56,11 @@ protected:
|
||||
// [GPUTask]
|
||||
Result run(GPUTasksContext* context) override
|
||||
{
|
||||
if (_buffer.IsMissing())
|
||||
if (!_buffer)
|
||||
return Result::MissingResources;
|
||||
|
||||
context->GPU->UpdateBuffer(_buffer, _data.Get(), _data.Length(), _offset);
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
void OnEnd() override
|
||||
{
|
||||
_buffer.Unlink();
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
, _rowPitch(rowPitch)
|
||||
, _slicePitch(slicePitch)
|
||||
{
|
||||
_texture.OnUnload.Bind<GPUUploadTextureMipTask, &GPUUploadTextureMipTask::OnResourceUnload>(this);
|
||||
_texture.Released.Bind<GPUUploadTextureMipTask, &GPUUploadTextureMipTask::OnResourceReleased>(this);
|
||||
|
||||
if (copyData)
|
||||
_data.Copy(data);
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResourceUnload(GPUTextureReference* ref)
|
||||
void OnResourceReleased()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
@@ -591,37 +591,37 @@ API_ENUM() enum class Quality : byte
|
||||
API_ENUM() enum class MaterialPostFxLocation : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The 'after' post processing pass using LDR input frame.
|
||||
/// Render the material after the post processing pass using *LDR* input frame.
|
||||
/// </summary>
|
||||
AfterPostProcessingPass = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The 'before' post processing pass using HDR input frame.
|
||||
/// Render the material before the post processing pass using *HDR* input frame.
|
||||
/// </summary>
|
||||
BeforePostProcessingPass = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The 'before' forward pass but after GBuffer with HDR input frame.
|
||||
/// Render the material before the forward pass but after *GBuffer* with *HDR* input frame.
|
||||
/// </summary>
|
||||
BeforeForwardPass = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The 'after' custom post effects.
|
||||
/// Render the material after custom post effects (scripted).
|
||||
/// </summary>
|
||||
AfterCustomPostEffects = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The 'before' Reflections pass. After the Light pass. Can be used to implement a custom light types that accumulate lighting to the light buffer.
|
||||
/// Render the material before the reflections pass but after the lighting pass using *HDR* input frame. It can be used to implement a custom light types that accumulate lighting to the light buffer.
|
||||
/// </summary>
|
||||
BeforeReflectionsPass = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The 'after' AA filter pass. Rendering is done to the output backbuffer.
|
||||
/// Render the material after anti-aliasing into the output backbuffer.
|
||||
/// </summary>
|
||||
AfterAntiAliasingPass = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The 'after' forward pass but before any post processing.
|
||||
/// Render the material after the forward pass but before any post processing.
|
||||
/// </summary>
|
||||
AfterForwardPass = 6,
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "GPUDevice.h"
|
||||
#include "RenderTargetPool.h"
|
||||
#include "GPUPipelineState.h"
|
||||
#include "GPUResourceProperty.h"
|
||||
#include "GPUSwapChain.h"
|
||||
#include "RenderTask.h"
|
||||
#include "RenderTools.h"
|
||||
@@ -25,6 +26,39 @@
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Scripting/Enums.h"
|
||||
|
||||
GPUResourcePropertyBase::~GPUResourcePropertyBase()
|
||||
{
|
||||
const auto e = _resource;
|
||||
if (e)
|
||||
{
|
||||
_resource = nullptr;
|
||||
e->Releasing.Unbind<GPUResourcePropertyBase, &GPUResourcePropertyBase::OnReleased>(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUResourcePropertyBase::OnSet(GPUResource* resource)
|
||||
{
|
||||
auto e = _resource;
|
||||
if (e != resource)
|
||||
{
|
||||
if (e)
|
||||
e->Releasing.Unbind<GPUResourcePropertyBase, &GPUResourcePropertyBase::OnReleased>(this);
|
||||
_resource = e = resource;
|
||||
if (e)
|
||||
e->Releasing.Bind<GPUResourcePropertyBase, &GPUResourcePropertyBase::OnReleased>(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUResourcePropertyBase::OnReleased()
|
||||
{
|
||||
auto e = _resource;
|
||||
if (e)
|
||||
{
|
||||
_resource = nullptr;
|
||||
e->Releasing.Unbind<GPUResourcePropertyBase, &GPUResourcePropertyBase::OnReleased>(this);
|
||||
}
|
||||
}
|
||||
|
||||
GPUPipelineState* GPUPipelineState::Spawn(const SpawnParams& params)
|
||||
{
|
||||
return GPUDevice::Instance->CreatePipelineState();
|
||||
@@ -313,6 +347,8 @@ bool GPUDevice::Init()
|
||||
|
||||
_res->TasksManager.SetExecutor(CreateTasksExecutor());
|
||||
LOG(Info, "Total graphics memory: {0}", Utilities::BytesToText(TotalGraphicsMemory));
|
||||
if (!Limits.HasCompute)
|
||||
LOG(Warning, "Compute Shaders are not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,28 +8,39 @@
|
||||
/// <summary>
|
||||
/// GPU Resource container utility object.
|
||||
/// </summary>
|
||||
template<typename T = GPUResource>
|
||||
class GPUResourceProperty
|
||||
class FLAXENGINE_API GPUResourcePropertyBase
|
||||
{
|
||||
private:
|
||||
T* _resource;
|
||||
protected:
|
||||
GPUResource* _resource = nullptr;
|
||||
|
||||
private:
|
||||
// Disable copy actions
|
||||
GPUResourceProperty(const GPUResourceProperty& other) = delete;
|
||||
public:
|
||||
NON_COPYABLE(GPUResourcePropertyBase);
|
||||
|
||||
GPUResourcePropertyBase() = default;
|
||||
~GPUResourcePropertyBase();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Action fired when resource gets unloaded (reference gets cleared bu async tasks should stop execution).
|
||||
/// Action fired when resource gets released (reference gets cleared bu async tasks should stop execution).
|
||||
/// </summary>
|
||||
Delegate<GPUResourceProperty*> OnUnload;
|
||||
Action Released;
|
||||
|
||||
protected:
|
||||
void OnSet(GPUResource* resource);
|
||||
void OnReleased();
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// GPU Resource container utility object.
|
||||
/// </summary>
|
||||
template<typename T = GPUResource>
|
||||
class GPUResourceProperty : public GPUResourcePropertyBase
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUResourceProperty"/> class.
|
||||
/// </summary>
|
||||
GPUResourceProperty()
|
||||
: _resource(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -38,9 +49,37 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource.</param>
|
||||
GPUResourceProperty(T* resource)
|
||||
: _resource(nullptr)
|
||||
{
|
||||
Set(resource);
|
||||
OnSet(resource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUResourceProperty"/> class.
|
||||
/// </summary>
|
||||
/// <param name="other">The other value.</param>
|
||||
GPUResourceProperty(const GPUResourceProperty& other)
|
||||
{
|
||||
OnSet(other.Get());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GPUResourceProperty"/> class.
|
||||
/// </summary>
|
||||
/// <param name="other">The other value.</param>
|
||||
GPUResourceProperty(GPUResourceProperty&& other)
|
||||
{
|
||||
OnSet(other.Get());
|
||||
other.OnSet(nullptr);
|
||||
}
|
||||
|
||||
GPUResourceProperty& operator=(GPUResourceProperty&& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
OnSet(other._resource);
|
||||
other.OnSet(nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -48,13 +87,6 @@ public:
|
||||
/// </summary>
|
||||
~GPUResourceProperty()
|
||||
{
|
||||
// Check if object has been binded
|
||||
if (_resource)
|
||||
{
|
||||
// Unlink
|
||||
_resource->Releasing.template Unbind<GPUResourceProperty, &GPUResourceProperty::onResourceUnload>(this);
|
||||
_resource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -63,43 +95,34 @@ public:
|
||||
return Get() == other;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator==(GPUResourceProperty& other) const
|
||||
FORCE_INLINE bool operator==(const GPUResourceProperty& other) const
|
||||
{
|
||||
return Get() == other.Get();
|
||||
}
|
||||
|
||||
GPUResourceProperty& operator=(const GPUResourceProperty& other)
|
||||
{
|
||||
if (this != &other)
|
||||
Set(other.Get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE GPUResourceProperty& operator=(T& other)
|
||||
{
|
||||
Set(&other);
|
||||
OnSet(&other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE GPUResourceProperty& operator=(T* other)
|
||||
{
|
||||
Set(other);
|
||||
OnSet(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to GPU Resource
|
||||
/// </summary>
|
||||
/// <returns>Resource</returns>
|
||||
FORCE_INLINE operator T*() const
|
||||
{
|
||||
return _resource;
|
||||
return (T*)_resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to resource
|
||||
/// </summary>
|
||||
/// <returns>True if resource has been binded, otherwise false</returns>
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return _resource != nullptr;
|
||||
@@ -108,37 +131,17 @@ public:
|
||||
/// <summary>
|
||||
/// Implicit conversion to resource
|
||||
/// </summary>
|
||||
/// <returns>Resource</returns>
|
||||
FORCE_INLINE T* operator->() const
|
||||
{
|
||||
return _resource;
|
||||
return (T*)_resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets linked resource
|
||||
/// </summary>
|
||||
/// <returns>Resource</returns>
|
||||
FORCE_INLINE T* Get() const
|
||||
{
|
||||
return _resource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if resource has been binded
|
||||
/// </summary>
|
||||
/// <returns>True if resource has been binded, otherwise false</returns>
|
||||
FORCE_INLINE bool IsBinded() const
|
||||
{
|
||||
return _resource != nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if resource is missing
|
||||
/// </summary>
|
||||
/// <returns>True if resource is missing, otherwise false</returns>
|
||||
FORCE_INLINE bool IsMissing() const
|
||||
{
|
||||
return _resource == nullptr;
|
||||
return (T*)_resource;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -148,19 +151,7 @@ public:
|
||||
/// <param name="value">Value to assign</param>
|
||||
void Set(T* value)
|
||||
{
|
||||
if (_resource != value)
|
||||
{
|
||||
// Remove reference from the old one
|
||||
if (_resource)
|
||||
_resource->Releasing.template Unbind<GPUResourceProperty, &GPUResourceProperty::onResourceUnload>(this);
|
||||
|
||||
// Change referenced object
|
||||
_resource = value;
|
||||
|
||||
// Add reference to the new one
|
||||
if (_resource)
|
||||
_resource->Releasing.template Bind<GPUResourceProperty, &GPUResourceProperty::onResourceUnload>(this);
|
||||
}
|
||||
OnSet(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -168,22 +159,7 @@ public:
|
||||
/// </summary>
|
||||
void Unlink()
|
||||
{
|
||||
if (_resource)
|
||||
{
|
||||
// Remove reference from the old one
|
||||
_resource->Releasing.template Unbind<GPUResourceProperty, &GPUResourceProperty::onResourceUnload>(this);
|
||||
_resource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void onResourceUnload()
|
||||
{
|
||||
if (_resource)
|
||||
{
|
||||
_resource = nullptr;
|
||||
OnUnload(this);
|
||||
}
|
||||
OnSet(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ void GraphicsSettings::Apply()
|
||||
Graphics::AllowCSMBlending = AllowCSMBlending;
|
||||
Graphics::GlobalSDFQuality = GlobalSDFQuality;
|
||||
Graphics::GIQuality = GIQuality;
|
||||
Graphics::PostProcessSettings = PostProcessSettings;
|
||||
Graphics::PostProcessSettings = ::PostProcessSettings();
|
||||
Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f);
|
||||
}
|
||||
|
||||
void Graphics::DisposeDevice()
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Enums.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
|
||||
/// <summary>
|
||||
/// Material domain type. Material domain defines the target usage of the material shader.
|
||||
@@ -86,7 +85,7 @@ API_ENUM() enum class MaterialBlendMode : byte
|
||||
API_ENUM() enum class MaterialShadingModel : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The unlit material. Emissive channel is used as an output color. Can perform custom lighting operations or just glow. Won't be affected by the lighting pipeline.
|
||||
/// The unlit material. The emissive channel is used as an output color. Can perform custom lighting operations or just glow. Won't be affected by the lighting pipeline.
|
||||
/// </summary>
|
||||
Unlit = 0,
|
||||
|
||||
@@ -96,7 +95,7 @@ API_ENUM() enum class MaterialShadingModel : byte
|
||||
Lit = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The subsurface material. Intended for materials like vax or skin that need light scattering to transport simulation through the object.
|
||||
/// The subsurface material. Intended for materials like wax or skin that need light scattering to transport simulation through the object.
|
||||
/// </summary>
|
||||
Subsurface = 2,
|
||||
|
||||
@@ -366,12 +365,12 @@ API_ENUM() enum class MaterialDecalBlendingMode : byte
|
||||
API_ENUM() enum class MaterialTransparentLightingMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Default directional lighting evaluated per-pixel at the material surface. Use it for semi-transparent surfaces - with both diffuse and specular lighting component active.
|
||||
/// Default directional lighting evaluated per-pixel at the material surface. Use it for semi-transparent surfaces - with both diffuse and specular lighting components active.
|
||||
/// </summary>
|
||||
Surface = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Non-directional lighting evaluated per-pixel at material surface. Use it for volumetric objects such as smoke, rain or dust - only diffuse lighting term is active (no specular highlights).
|
||||
/// Non-directional lighting evaluated per-pixel at material surface. Use it for volumetric objects such as smoke, rain or dust - only the diffuse lighting term is active (no specular highlights).
|
||||
/// </summary>
|
||||
SurfaceNonDirectional = 1,
|
||||
};
|
||||
|
||||
@@ -90,6 +90,21 @@ public:
|
||||
/// </summary>
|
||||
Array<BlendShape> BlendShapes;
|
||||
|
||||
/// <summary>
|
||||
/// Global translation for this mesh to be at it's local origin.
|
||||
/// </summary>
|
||||
Vector3 OriginTranslation = Vector3::Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Orientation for this mesh at it's local origin.
|
||||
/// </summary>
|
||||
Quaternion OriginOrientation = Quaternion::Identity;
|
||||
|
||||
/// <summary>
|
||||
/// Meshes scaling.
|
||||
/// </summary>
|
||||
Vector3 Scaling = Vector3::One;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Determines whether this instance has any mesh data.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Math/Vector4.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Content/SoftAssetReference.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Content/Assets/Texture.h"
|
||||
#include "Engine/Content/Assets/MaterialBase.h"
|
||||
@@ -446,13 +447,13 @@ API_STRUCT() struct FLAXENGINE_API BloomSettings : ISerializable
|
||||
bool Enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// Bloom effect strength. Value 0 disabled is, while higher values increase the effect.
|
||||
/// Bloom effect strength. Set a value of 0 to disabled it, while higher values increase the effect.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 20.0f, 0.01f), EditorOrder(1), PostProcessSetting((int)BloomSettingsOverride.Intensity)")
|
||||
float Intensity = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum pixel brightness value to start blowing. Values below the threshold are skipped.
|
||||
/// Minimum pixel brightness value to start blooming. Values below this threshold are skipped.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 15.0f, 0.01f), EditorOrder(2), PostProcessSetting((int)BloomSettingsOverride.Threshold)")
|
||||
float Threshold = 3.0f;
|
||||
@@ -850,7 +851,7 @@ API_STRUCT() struct FLAXENGINE_API ColorGradingSettings : ISerializable
|
||||
/// The Lookup Table (LUT) used to perform color correction.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(22), PostProcessSetting((int)ColorGradingSettingsOverride.LutTexture)")
|
||||
AssetReference<Texture> LutTexture;
|
||||
SoftAssetReference<Texture> LutTexture;
|
||||
|
||||
/// <summary>
|
||||
/// The LUT blending weight (normalized to range 0-1). Default is 1.0.
|
||||
@@ -986,13 +987,13 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable
|
||||
float MaxBrightness = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The lower bound for the luminance histogram of the scene color. Value is in percent and limits the pixels below this brightness. Use values from range 60-80. Used only in AutomaticHistogram mode.
|
||||
/// The lower bound for the luminance histogram of the scene color. This value is in percent and limits the pixels below this brightness. Use values in the range of 60-80. Used only in AutomaticHistogram mode.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(1, 99, 0.001f), EditorOrder(3), PostProcessSetting((int)EyeAdaptationSettingsOverride.HistogramLowPercent)")
|
||||
float HistogramLowPercent = 70.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The upper bound for the luminance histogram of the scene color. Value is in percent and limits the pixels above this brightness. Use values from range 80-95. Used only in AutomaticHistogram mode.
|
||||
/// The upper bound for the luminance histogram of the scene color. This value is in percent and limits the pixels above this brightness. Use values in the range of 80-95. Used only in AutomaticHistogram mode.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(1, 99, 0.001f), EditorOrder(3), PostProcessSetting((int)EyeAdaptationSettingsOverride.HistogramHighPercent)")
|
||||
float HistogramHighPercent = 98.0f;
|
||||
@@ -1090,13 +1091,13 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
|
||||
Float3 VignetteColor = Float3(0, 0, 0.001f);
|
||||
|
||||
/// <summary>
|
||||
/// Controls shape of the vignette. Values near 0 produce rectangle shape. Higher values result in round shape. The default value is 0.125.
|
||||
/// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0001f, 2.0f, 0.001f), EditorOrder(2), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteShapeFactor)")
|
||||
float VignetteShapeFactor = 0.125f;
|
||||
|
||||
/// <summary>
|
||||
/// Intensity of the grain filter. Value 0 hides it. The default value is 0.005.
|
||||
/// Intensity of the grain filter. A value of 0 hides it. The default value is 0.005.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0f, 2.0f, 0.005f), EditorOrder(3), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainAmount)")
|
||||
float GrainAmount = 0.006f;
|
||||
@@ -1108,19 +1109,19 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
|
||||
float GrainParticleSize = 1.6f;
|
||||
|
||||
/// <summary>
|
||||
/// Speed of the grain particles animation.
|
||||
/// Speed of the grain particle animation.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0f, 10.0f, 0.01f), EditorOrder(5), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainSpeed)")
|
||||
float GrainSpeed = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Controls chromatic aberration effect strength. Value 0 hides it.
|
||||
/// Controls the chromatic aberration effect strength. A value of 0 hides it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0f, 1.0f, 0.01f), EditorOrder(6), PostProcessSetting((int)CameraArtifactsSettingsOverride.ChromaticDistortion)")
|
||||
float ChromaticDistortion = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Screen tint color (alpha channel defines the blending factor).
|
||||
/// Screen tint color (the alpha channel defines the blending factor).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(typeof(Color), \"0,0,0,0\"), EditorOrder(7), PostProcessSetting((int)CameraArtifactsSettingsOverride.ScreenFadeColor)")
|
||||
Color ScreenFadeColor = Color::Transparent;
|
||||
@@ -1226,7 +1227,7 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
|
||||
LensFlaresSettingsOverride OverrideFlags = Override::None;
|
||||
|
||||
/// <summary>
|
||||
/// Strength of the effect. Value 0 disabled it.
|
||||
/// Strength of the effect. A value of 0 disables it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(0), PostProcessSetting((int)LensFlaresSettingsOverride.Intensity)")
|
||||
float Intensity = 1.0f;
|
||||
@@ -1277,10 +1278,10 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
|
||||
/// Fullscreen lens dirt texture.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(8), PostProcessSetting((int)LensFlaresSettingsOverride.LensDirt)")
|
||||
AssetReference<Texture> LensDirt;
|
||||
SoftAssetReference<Texture> LensDirt;
|
||||
|
||||
/// <summary>
|
||||
/// Fullscreen lens dirt intensity parameter. Allows to tune dirt visibility.
|
||||
/// Fullscreen lens dirt intensity parameter. Allows tuning dirt visibility.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 100, 0.01f), EditorOrder(9), PostProcessSetting((int)LensFlaresSettingsOverride.LensDirtIntensity)")
|
||||
float LensDirtIntensity = 1.0f;
|
||||
@@ -1289,13 +1290,13 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
|
||||
/// Custom lens color texture (1D) used for lens color spectrum.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(10), PostProcessSetting((int)LensFlaresSettingsOverride.LensColor)")
|
||||
AssetReference<Texture> LensColor;
|
||||
SoftAssetReference<Texture> LensColor;
|
||||
|
||||
/// <summary>
|
||||
/// Custom lens star texture sampled by lens flares.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(11), PostProcessSetting((int)LensFlaresSettingsOverride.LensStar)")
|
||||
AssetReference<Texture> LensStar;
|
||||
SoftAssetReference<Texture> LensStar;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -1418,13 +1419,13 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
|
||||
DepthOfFieldSettingsOverride OverrideFlags = Override::None;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, depth of field effect will be visible.
|
||||
/// If checked, the depth of field effect will be visible.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)DepthOfFieldSettingsOverride.Enabled)")
|
||||
bool Enabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// The blur intensity in the out-of-focus areas. Allows reducing blur amount by scaling down the Gaussian Blur radius. Normalized to range 0-1.
|
||||
/// The blur intensity in the out-of-focus areas. Allows reducing the blur amount by scaling down the Gaussian Blur radius. Normalized to range 0-1.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 1, 0.01f), EditorOrder(1), PostProcessSetting((int)DepthOfFieldSettingsOverride.BlurStrength)")
|
||||
float BlurStrength = 1.0f;
|
||||
@@ -1478,7 +1479,7 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
|
||||
float BokehBrightness = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Defines bokeh shapes type.
|
||||
/// Defines the type of the bokeh shapes.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehShape)")
|
||||
BokehShapeType BokehShape = BokehShapeType::Octagon;
|
||||
@@ -1487,22 +1488,22 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
|
||||
/// If BokehShape is set to Custom, then this texture will be used for the bokeh shapes. For best performance, use small, compressed, grayscale textures (for instance 32px).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(null), EditorOrder(11), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehShapeCustom)")
|
||||
AssetReference<Texture> BokehShapeCustom;
|
||||
SoftAssetReference<Texture> BokehShapeCustom;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum pixel brightness to create bokeh. Pixels with lower brightness will be skipped.
|
||||
/// The minimum pixel brightness to create the bokeh. Pixels with lower brightness will be skipped.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 10000.0f, 0.01f), EditorOrder(12), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehBrightnessThreshold)")
|
||||
float BokehBrightnessThreshold = 3.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Depth of Field bokeh shapes blur threshold.
|
||||
/// Depth of Field bokeh shape blur threshold.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 1.0f, 0.001f), EditorOrder(13), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehBlurThreshold)")
|
||||
float BokehBlurThreshold = 0.05f;
|
||||
|
||||
/// <summary>
|
||||
/// Controls bokeh shapes brightness falloff. Higher values reduce bokeh visibility.
|
||||
/// Controls bokeh shape brightness falloff. Higher values reduce bokeh visibility.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 2.0f, 0.001f), EditorOrder(14), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehFalloff)")
|
||||
float BokehFalloff = 0.5f;
|
||||
@@ -1574,25 +1575,25 @@ API_STRUCT() struct FLAXENGINE_API MotionBlurSettings : ISerializable
|
||||
MotionBlurSettingsOverride OverrideFlags = Override::None;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, motion blur effect will be rendered.
|
||||
/// If checked, the motion blur effect will be rendered.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)MotionBlurSettingsOverride.Enabled)")
|
||||
bool Enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// The blur effect strength. Value 0 disabled is, while higher values increase the effect.
|
||||
/// The blur effect strength. A value of 0 disables it, while higher values increase the effect.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 5, 0.01f), EditorOrder(1), PostProcessSetting((int)MotionBlurSettingsOverride.Scale)")
|
||||
float Scale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of sample points used during motion blur rendering. It affects quality and performance.
|
||||
/// The amount of sample points used during motion blur rendering. It affects blur quality and performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(4, 32, 0.1f), EditorOrder(2), PostProcessSetting((int)MotionBlurSettingsOverride.SampleCount)")
|
||||
int32 SampleCount = 10;
|
||||
|
||||
/// <summary>
|
||||
/// The motion vectors texture resolution. Motion blur uses per-pixel motion vectors buffer that contains objects movement information. Use lower resolution to improve performance.
|
||||
/// The motion vectors texture resolution. Motion blur uses a per-pixel motion vector buffer that contains an objects movement information. Use a lower resolution to improve performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(3), PostProcessSetting((int)MotionBlurSettingsOverride.MotionVectorsResolution)")
|
||||
ResolutionMode MotionVectorsResolution = ResolutionMode::Half;
|
||||
@@ -1897,13 +1898,13 @@ API_STRUCT() struct FLAXENGINE_API AntiAliasingSettings : ISerializable
|
||||
float TAA_Sharpness = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The blend coefficient for stationary fragments. Controls the percentage of history sample blended into final color for fragments with minimal active motion.
|
||||
/// The blend coefficient for stationary fragments. Controls the percentage of history samples blended into the final color for fragments with minimal active motion.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(3), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_StationaryBlending), EditorDisplay(null, \"TAA Stationary Blending\")")
|
||||
float TAA_StationaryBlending = 0.95f;
|
||||
|
||||
/// <summary>
|
||||
/// The blending coefficient for moving fragments. Controls the percentage of history sample blended into the final color for fragments with significant active motion.
|
||||
/// The blending coefficient for moving fragments. Controls the percentage of history samples blended into the final color for fragments with significant active motion.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(4), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_MotionBlending), EditorDisplay(null, \"TAA Motion Blending\")")
|
||||
float TAA_MotionBlending = 0.7f;
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#include "Engine/Debug/DebugLog.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Level/Actors/Camera.h"
|
||||
#include "Engine/Level/Actors/PostFxVolume.h"
|
||||
#include "Engine/Renderer/Renderer.h"
|
||||
#include "Engine/Render2D/Render2D.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Level/Actors/PostFxVolume.h"
|
||||
#include "Engine/Profiler/Profiler.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
@@ -202,15 +203,21 @@ void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
|
||||
{
|
||||
Level::CollectPostFxVolumes(renderContext);
|
||||
}
|
||||
if (EnumHasAllFlags(ActorsSource , ActorsSources::CustomActors))
|
||||
if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomActors))
|
||||
{
|
||||
for (Actor* a : CustomActors)
|
||||
{
|
||||
auto* postFxVolume = dynamic_cast<PostFxVolume*>(a);
|
||||
if (postFxVolume && a->GetIsActive())
|
||||
{
|
||||
postFxVolume->Collect(renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomScenes))
|
||||
{
|
||||
for (Scene* scene : CustomScenes)
|
||||
{
|
||||
if (scene && scene->IsActiveInHierarchy())
|
||||
scene->Rendering.CollectPostFxVolumes(renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,6 +289,14 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContextBatch& renderContextBatch,
|
||||
ASSERT_LOW_LAYER(_customActorsScene);
|
||||
_customActorsScene->Draw(renderContextBatch, (SceneRendering::DrawCategory)category);
|
||||
}
|
||||
if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomScenes))
|
||||
{
|
||||
for (Scene* scene : CustomScenes)
|
||||
{
|
||||
if (scene && scene->IsActiveInHierarchy())
|
||||
scene->Rendering.Draw(renderContextBatch, (SceneRendering::DrawCategory)category);
|
||||
}
|
||||
}
|
||||
if (EnumHasAllFlags(ActorsSource, ActorsSources::Scenes))
|
||||
{
|
||||
Level::DrawActors(renderContextBatch, category);
|
||||
|
||||
@@ -21,6 +21,7 @@ class PostProcessEffect;
|
||||
struct RenderContext;
|
||||
class Camera;
|
||||
class Actor;
|
||||
class Scene;
|
||||
|
||||
/// <summary>
|
||||
/// Allows to perform custom rendering using graphics pipeline.
|
||||
@@ -174,6 +175,11 @@ API_ENUM(Attributes="Flags") enum class ActorsSources
|
||||
/// </summary>
|
||||
CustomActors = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The scenes from the custom collection.
|
||||
/// </summary>
|
||||
CustomScenes = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The actors from the loaded scenes and custom collection.
|
||||
/// </summary>
|
||||
@@ -267,9 +273,14 @@ public:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The custom set of actors to render.
|
||||
/// The custom set of actors to render. Used when ActorsSources::CustomActors flag is active.
|
||||
/// </summary>
|
||||
Array<Actor*> CustomActors;
|
||||
API_FIELD() Array<Actor*> CustomActors;
|
||||
|
||||
/// <summary>
|
||||
/// The custom set of scenes to render. Used when ActorsSources::CustomScenes flag is active.
|
||||
/// </summary>
|
||||
API_FIELD() Array<Scene*> CustomScenes;
|
||||
|
||||
/// <summary>
|
||||
/// Adds the custom actor to the rendering.
|
||||
|
||||
@@ -87,7 +87,11 @@ bool GPUShader::Create(MemoryReadStream& stream)
|
||||
GPUShaderProgramInitializer initializer;
|
||||
#if !BUILD_RELEASE
|
||||
initializer.Owner = this;
|
||||
const StringView name = GetName();
|
||||
#else
|
||||
const StringView name;
|
||||
#endif
|
||||
const bool hasCompute = GPUDevice::Instance->Limits.HasCompute;
|
||||
for (int32 i = 0; i < shadersCount; i++)
|
||||
{
|
||||
const ShaderStage type = static_cast<ShaderStage>(stream.ReadByte());
|
||||
@@ -117,10 +121,15 @@ bool GPUShader::Create(MemoryReadStream& stream)
|
||||
stream.ReadBytes(&initializer.Bindings, sizeof(ShaderBindings));
|
||||
|
||||
// Create shader program
|
||||
if (type == ShaderStage::Compute && !hasCompute)
|
||||
{
|
||||
LOG(Warning, "Failed to create {} Shader program '{}' ({}).", ::ToString(type), String(initializer.Name), name);
|
||||
continue;
|
||||
}
|
||||
GPUShaderProgram* shader = CreateGPUShaderProgram(type, initializer, cache, cacheSize, stream);
|
||||
if (shader == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to create {} Shader program '{}'.", ::ToString(type), String(initializer.Name));
|
||||
LOG(Error, "Failed to create {} Shader program '{}' ({}).", ::ToString(type), String(initializer.Name), name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,19 @@ public:
|
||||
/// </summary>
|
||||
class GPUShaderProgramVS : public GPUShaderProgram
|
||||
{
|
||||
public:
|
||||
// Input element run-time data (see VertexShaderMeta::InputElement for compile-time data)
|
||||
PACK_STRUCT(struct InputElement
|
||||
{
|
||||
byte Type; // VertexShaderMeta::InputType
|
||||
byte Index;
|
||||
byte Format; // PixelFormat
|
||||
byte InputSlot;
|
||||
uint32 AlignedByteOffset; // Fixed value or INPUT_LAYOUT_ELEMENT_ALIGN if auto
|
||||
byte InputSlotClass; // INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA or INPUT_LAYOUT_ELEMENT_PER_INSTANCE_DATA
|
||||
uint32 InstanceDataStepRate; // 0 if per-vertex
|
||||
});
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets input layout description handle (platform dependent).
|
||||
|
||||
@@ -22,10 +22,10 @@ TextureHeader::TextureHeader()
|
||||
TextureGroup = -1;
|
||||
}
|
||||
|
||||
TextureHeader::TextureHeader(TextureHeader_Deprecated& old)
|
||||
TextureHeader::TextureHeader(const TextureHeader_Deprecated& old)
|
||||
{
|
||||
Platform::MemoryClear(this, sizeof(*this));
|
||||
Width = old.Width;;
|
||||
Width = old.Width;
|
||||
Height = old.Height;
|
||||
MipLevels = old.MipLevels;
|
||||
Format = old.Format;
|
||||
@@ -49,7 +49,7 @@ StreamingTexture::StreamingTexture(ITextureOwner* parent, const String& name)
|
||||
, _texture(nullptr)
|
||||
, _isBlockCompressed(false)
|
||||
{
|
||||
ASSERT(_owner != nullptr);
|
||||
ASSERT(parent != nullptr);
|
||||
|
||||
// Always have created texture object
|
||||
ASSERT(GPUDevice::Instance);
|
||||
@@ -63,7 +63,6 @@ StreamingTexture::~StreamingTexture()
|
||||
{
|
||||
UnloadTexture();
|
||||
SAFE_DELETE(_texture);
|
||||
ASSERT(_streamingTasks.Count() == 0);
|
||||
}
|
||||
|
||||
Float2 StreamingTexture::Size() const
|
||||
@@ -134,11 +133,9 @@ bool StreamingTexture::Create(const TextureHeader& header)
|
||||
void StreamingTexture::UnloadTexture()
|
||||
{
|
||||
ScopeLock lock(_owner->GetOwnerLocker());
|
||||
|
||||
// Release
|
||||
CancelStreamingTasks();
|
||||
_texture->ReleaseGPU();
|
||||
_header.MipLevels = 0;
|
||||
|
||||
ASSERT(_streamingTasks.Count() == 0);
|
||||
}
|
||||
|
||||
@@ -329,11 +326,11 @@ public:
|
||||
, _dataLock(_streamingTexture->GetOwner()->LockData())
|
||||
{
|
||||
_streamingTexture->_streamingTasks.Add(this);
|
||||
_texture.OnUnload.Bind<StreamTextureMipTask, &StreamTextureMipTask::onResourceUnload2>(this);
|
||||
_texture.Released.Bind<StreamTextureMipTask, &StreamTextureMipTask::OnResourceReleased2>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void onResourceUnload2(GPUTextureReference* ref)
|
||||
void OnResourceReleased2()
|
||||
{
|
||||
// Unlink texture
|
||||
if (_streamingTexture)
|
||||
|
||||
@@ -660,6 +660,7 @@ uint64 TextureBase::GetMemoryUsage() const
|
||||
|
||||
void TextureBase::CancelStreaming()
|
||||
{
|
||||
Asset::CancelStreaming();
|
||||
_texture.CancelStreamingTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -106,5 +106,5 @@ struct FLAXENGINE_API TextureHeader
|
||||
byte CustomData[10];
|
||||
|
||||
TextureHeader();
|
||||
TextureHeader(TextureHeader_Deprecated& old);
|
||||
TextureHeader(const TextureHeader_Deprecated& old);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPUResource.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "../GPUDeviceDX.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
|
||||
@@ -15,32 +15,21 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
{
|
||||
D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
|
||||
// Temporary variables
|
||||
byte Type, Format, Index, InputSlot, InputSlotClass;
|
||||
uint32 AlignedByteOffset, InstanceDataStepRate;
|
||||
|
||||
// Load Input Layout (it may be empty)
|
||||
// Load Input Layout
|
||||
byte inputLayoutSize;
|
||||
stream.ReadByte(&inputLayoutSize);
|
||||
ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS);
|
||||
D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
for (int32 a = 0; a < inputLayoutSize; a++)
|
||||
{
|
||||
// Read description
|
||||
// TODO: maybe use struct and load at once?
|
||||
stream.ReadByte(&Type);
|
||||
stream.ReadByte(&Index);
|
||||
stream.ReadByte(&Format);
|
||||
stream.ReadByte(&InputSlot);
|
||||
stream.ReadUint32(&AlignedByteOffset);
|
||||
stream.ReadByte(&InputSlotClass);
|
||||
stream.ReadUint32(&InstanceDataStepRate);
|
||||
GPUShaderProgramVS::InputElement inputElement;
|
||||
stream.Read(inputElement);
|
||||
|
||||
// Get semantic name
|
||||
const char* semanticName = nullptr;
|
||||
// TODO: maybe use enum+mapping ?
|
||||
switch (Type)
|
||||
switch (inputElement.Type)
|
||||
{
|
||||
case 1:
|
||||
semanticName = "POSITION";
|
||||
@@ -70,7 +59,7 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
|
||||
semanticName = "BLENDWEIGHT";
|
||||
break;
|
||||
default:
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type);
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", inputElement.Type);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -78,12 +67,12 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
|
||||
inputLayoutDesc[a] =
|
||||
{
|
||||
semanticName,
|
||||
static_cast<UINT>(Index),
|
||||
static_cast<DXGI_FORMAT>(Format),
|
||||
static_cast<UINT>(InputSlot),
|
||||
static_cast<UINT>(AlignedByteOffset),
|
||||
static_cast<D3D11_INPUT_CLASSIFICATION>(InputSlotClass),
|
||||
static_cast<UINT>(InstanceDataStepRate)
|
||||
static_cast<UINT>(inputElement.Index),
|
||||
static_cast<DXGI_FORMAT>(inputElement.Format),
|
||||
static_cast<UINT>(inputElement.InputSlot),
|
||||
static_cast<UINT>(inputElement.AlignedByteOffset),
|
||||
static_cast<D3D11_INPUT_CLASSIFICATION>(inputElement.InputSlotClass),
|
||||
static_cast<UINT>(inputElement.InstanceDataStepRate)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Graphics/GPUPipelineState.h"
|
||||
#include "GPUDeviceDX12.h"
|
||||
#include "Types.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
class GPUTextureViewDX12;
|
||||
|
||||
@@ -20,32 +20,21 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
{
|
||||
D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
|
||||
// Temporary variables
|
||||
byte Type, Format, Index, InputSlot, InputSlotClass;
|
||||
uint32 AlignedByteOffset, InstanceDataStepRate;
|
||||
|
||||
// Load Input Layout (it may be empty)
|
||||
byte inputLayoutSize;
|
||||
stream.ReadByte(&inputLayoutSize);
|
||||
ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS);
|
||||
D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
for (int32 a = 0; a < inputLayoutSize; a++)
|
||||
{
|
||||
// Read description
|
||||
// TODO: maybe use struct and load at once?
|
||||
stream.ReadByte(&Type);
|
||||
stream.ReadByte(&Index);
|
||||
stream.ReadByte(&Format);
|
||||
stream.ReadByte(&InputSlot);
|
||||
stream.ReadUint32(&AlignedByteOffset);
|
||||
stream.ReadByte(&InputSlotClass);
|
||||
stream.ReadUint32(&InstanceDataStepRate);
|
||||
GPUShaderProgramVS::InputElement inputElement;
|
||||
stream.Read(inputElement);
|
||||
|
||||
// Get semantic name
|
||||
const char* semanticName = nullptr;
|
||||
// TODO: maybe use enum+mapping ?
|
||||
switch (Type)
|
||||
switch (inputElement.Type)
|
||||
{
|
||||
case 1:
|
||||
semanticName = "POSITION";
|
||||
@@ -75,7 +64,7 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
|
||||
semanticName = "BLENDWEIGHT";
|
||||
break;
|
||||
default:
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type);
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", inputElement.Type);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -83,12 +72,12 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
|
||||
inputLayout[a] =
|
||||
{
|
||||
semanticName,
|
||||
static_cast<UINT>(Index),
|
||||
static_cast<DXGI_FORMAT>(Format),
|
||||
static_cast<UINT>(InputSlot),
|
||||
static_cast<UINT>(AlignedByteOffset),
|
||||
static_cast<D3D12_INPUT_CLASSIFICATION>(InputSlotClass),
|
||||
static_cast<UINT>(InstanceDataStepRate)
|
||||
static_cast<UINT>(inputElement.Index),
|
||||
static_cast<DXGI_FORMAT>(inputElement.Format),
|
||||
static_cast<UINT>(inputElement.InputSlot),
|
||||
static_cast<UINT>(inputElement.AlignedByteOffset),
|
||||
static_cast<D3D12_INPUT_CLASSIFICATION>(inputElement.InputSlotClass),
|
||||
static_cast<UINT>(inputElement.InstanceDataStepRate)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
|
||||
#if PLATFORM_DESKTOP
|
||||
#define VULKAN_UNIFORM_RING_BUFFER_SIZE 24 * 1024 * 1024
|
||||
#define VULKAN_UNIFORM_RING_BUFFER_SIZE (24 * 1024 * 1024)
|
||||
#else
|
||||
#define VULKAN_UNIFORM_RING_BUFFER_SIZE 8 * 1024 * 1024
|
||||
#define VULKAN_UNIFORM_RING_BUFFER_SIZE (8 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
UniformBufferUploaderVulkan::UniformBufferUploaderVulkan(GPUDeviceVulkan* device)
|
||||
@@ -153,10 +153,6 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
|
||||
vertexBindingDescriptions[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
}
|
||||
|
||||
// Temporary variables
|
||||
byte Type, Format, Index, InputSlot, InputSlotClass;
|
||||
uint32 AlignedByteOffset, InstanceDataStepRate;
|
||||
|
||||
// Load Input Layout (it may be empty)
|
||||
byte inputLayoutSize;
|
||||
stream.ReadByte(&inputLayoutSize);
|
||||
@@ -167,32 +163,26 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
|
||||
for (int32 a = 0; a < inputLayoutSize; a++)
|
||||
{
|
||||
// Read description
|
||||
// TODO: maybe use struct and load at once?
|
||||
stream.ReadByte(&Type);
|
||||
stream.ReadByte(&Index);
|
||||
stream.ReadByte(&Format);
|
||||
stream.ReadByte(&InputSlot);
|
||||
stream.ReadUint32(&AlignedByteOffset);
|
||||
stream.ReadByte(&InputSlotClass);
|
||||
stream.ReadUint32(&InstanceDataStepRate);
|
||||
GPUShaderProgramVS::InputElement inputElement;
|
||||
stream.Read(inputElement);
|
||||
|
||||
const auto size = PixelFormatExtensions::SizeInBytes((PixelFormat)Format);
|
||||
if (AlignedByteOffset != INPUT_LAYOUT_ELEMENT_ALIGN)
|
||||
offset = AlignedByteOffset;
|
||||
const auto size = PixelFormatExtensions::SizeInBytes((PixelFormat)inputElement.Format);
|
||||
if (inputElement.AlignedByteOffset != INPUT_LAYOUT_ELEMENT_ALIGN)
|
||||
offset = inputElement.AlignedByteOffset;
|
||||
|
||||
auto& vertexBindingDescription = vertexBindingDescriptions[InputSlot];
|
||||
vertexBindingDescription.binding = InputSlot;
|
||||
auto& vertexBindingDescription = vertexBindingDescriptions[inputElement.InputSlot];
|
||||
vertexBindingDescription.binding = inputElement.InputSlot;
|
||||
vertexBindingDescription.stride = Math::Max(vertexBindingDescription.stride, (uint32_t)(offset + size));
|
||||
vertexBindingDescription.inputRate = InputSlotClass == INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||
ASSERT(InstanceDataStepRate == 0 || InstanceDataStepRate == 1);
|
||||
vertexBindingDescription.inputRate = inputElement.InputSlotClass == INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||
ASSERT(inputElement.InstanceDataStepRate == 0 || inputElement.InstanceDataStepRate == 1);
|
||||
|
||||
auto& vertexAttributeDescription = vertexAttributeDescriptions[a];
|
||||
vertexAttributeDescription.location = a;
|
||||
vertexAttributeDescription.binding = InputSlot;
|
||||
vertexAttributeDescription.format = RenderToolsVulkan::ToVulkanFormat((PixelFormat)Format);
|
||||
vertexAttributeDescription.binding = inputElement.InputSlot;
|
||||
vertexAttributeDescription.format = RenderToolsVulkan::ToVulkanFormat((PixelFormat)inputElement.Format);
|
||||
vertexAttributeDescription.offset = offset;
|
||||
|
||||
bindingsCount = Math::Max(bindingsCount, (uint32)InputSlot + 1);
|
||||
bindingsCount = Math::Max(bindingsCount, (uint32)inputElement.InputSlot + 1);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
|
||||
@@ -1406,7 +1406,7 @@ Script* Actor::FindScript(const MClass* type) const
|
||||
CHECK_RETURN(type, nullptr);
|
||||
for (auto script : Scripts)
|
||||
{
|
||||
if (script->GetClass()->IsSubClassOf(type))
|
||||
if (script->GetClass()->IsSubClassOf(type) || script->GetClass()->HasInterface(type))
|
||||
return script;
|
||||
}
|
||||
for (auto child : Children)
|
||||
|
||||
@@ -33,12 +33,12 @@ API_CLASS(Abstract) class FLAXENGINE_API Actor : public SceneObject
|
||||
friend Prefab;
|
||||
friend PrefabInstanceData;
|
||||
protected:
|
||||
int16 _isActive : 1;
|
||||
int16 _isActiveInHierarchy : 1;
|
||||
int16 _isPrefabRoot : 1;
|
||||
int16 _isEnabled : 1;
|
||||
int16 _drawNoCulling : 1;
|
||||
int16 _drawCategory : 4;
|
||||
uint16 _isActive : 1;
|
||||
uint16 _isActiveInHierarchy : 1;
|
||||
uint16 _isPrefabRoot : 1;
|
||||
uint16 _isEnabled : 1;
|
||||
uint16 _drawNoCulling : 1;
|
||||
uint16 _drawCategory : 4;
|
||||
byte _layer;
|
||||
StaticFlags _staticFlags;
|
||||
Transform _localTransform;
|
||||
|
||||
@@ -172,6 +172,28 @@ void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& no
|
||||
GetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace);
|
||||
}
|
||||
|
||||
void AnimatedModel::SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace)
|
||||
{
|
||||
if (GraphInstance.NodesPose.IsEmpty())
|
||||
const_cast<AnimatedModel*>(this)->PreInitSkinningData(); // Ensure to have valid nodes pose to return
|
||||
CHECK(nodeIndex >= 0 && nodeIndex < GraphInstance.NodesPose.Count());
|
||||
GraphInstance.NodesPose[nodeIndex] = nodeTransformation;
|
||||
if (worldSpace)
|
||||
{
|
||||
Matrix world;
|
||||
_transform.GetWorld(world);
|
||||
Matrix invWorld;
|
||||
Matrix::Invert(world, invWorld);
|
||||
GraphInstance.NodesPose[nodeIndex] = GraphInstance.NodesPose[nodeIndex] * invWorld;
|
||||
}
|
||||
OnAnimationUpdated();
|
||||
}
|
||||
|
||||
void AnimatedModel::SetNodeTransformation(const StringView& nodeName, const Matrix& nodeTransformation, bool worldSpace)
|
||||
{
|
||||
SetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace);
|
||||
}
|
||||
|
||||
int32 AnimatedModel::FindClosestNode(const Vector3& location, bool worldSpace) const
|
||||
{
|
||||
if (GraphInstance.NodesPose.IsEmpty())
|
||||
|
||||
@@ -229,6 +229,22 @@ public:
|
||||
/// <param name="worldSpace">True if convert matrices into world-space, otherwise returned values will be in local-space of the actor.</param>
|
||||
API_FUNCTION() void GetNodeTransformation(const StringView& nodeName, API_PARAM(Out) Matrix& nodeTransformation, bool worldSpace = false) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the node final transformation. If multiple nodes are to be set within a frame, do not use set worldSpace to true, and do the conversion yourself to avoid recalculation of inv matrices.
|
||||
/// </summary>
|
||||
/// <param name="nodeIndex">The index of the skinned model skeleton node.</param>
|
||||
/// <param name="nodeTransformation">The final node transformation matrix.</param>
|
||||
/// <param name="worldSpace">True if convert matrices from world-space, otherwise values will be in local-space of the actor.</param>
|
||||
API_FUNCTION() void SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace = false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the node final transformation. If multiple nodes are to be set within a frame, do not use set worldSpace to true, and do the conversion yourself to avoid recalculation of inv matrices.
|
||||
/// </summary>
|
||||
/// <param name="nodeName">The name of the skinned model skeleton node.</param>
|
||||
/// <param name="nodeTransformation">The final node transformation matrix.</param>
|
||||
/// <param name="worldSpace">True if convert matrices from world-space, otherwise values will be in local-space of the actor.</param>
|
||||
API_FUNCTION() void SetNodeTransformation(const StringView& nodeName, const Matrix& nodeTransformation, bool worldSpace = false);
|
||||
|
||||
/// <summary>
|
||||
/// Finds the closest node to a given location.
|
||||
/// </summary>
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the value indicating if camera should use perspective rendering mode, otherwise it will use orthographic projection.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Camera\"), Tooltip(\"Enables perspective projection mode, otherwise uses orthographic.\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(true), EditorDisplay(\"Camera\")")
|
||||
bool GetUsePerspective() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the camera's field of view (in degrees).
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), Tooltip(\"Field of view angle in degrees.\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), VisibleIf(nameof(UsePerspective))")
|
||||
float GetFieldOfView() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the custom aspect ratio. 0 if not use custom value.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(0.0f), Limit(0, 10, 0.01f), EditorDisplay(\"Camera\"), Tooltip(\"Custom aspect ratio to use. Set to 0 to disable.\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(0.0f), Limit(0, 10, 0.01f), EditorDisplay(\"Camera\"), VisibleIf(nameof(UsePerspective))")
|
||||
float GetCustomAspectRatio() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets camera's near plane distance.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\"), Tooltip(\"Near clipping plane distance\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\")")
|
||||
float GetNearPlane() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets camera's far plane distance.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\"), Tooltip(\"Far clipping plane distance\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\")")
|
||||
float GetFarPlane() const;
|
||||
|
||||
/// <summary>
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the orthographic projection scale.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(1.0f), Limit(0.0001f, 1000, 0.01f), EditorDisplay(\"Camera\"), Tooltip(\"Orthographic projection scale\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(1.0f), Limit(0.0001f, 1000, 0.01f), EditorDisplay(\"Camera\"), VisibleIf(nameof(UsePerspective), true)")
|
||||
float GetOrthographicScale() const;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,7 +13,7 @@ class FLAXENGINE_API DirectionalLight : public LightWithShadow
|
||||
DECLARE_SCENE_OBJECT(DirectionalLight);
|
||||
public:
|
||||
/// <summary>
|
||||
/// The number of cascades used for slicing the range of depth covered by the light. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades.
|
||||
/// The number of cascades used for slicing the range of depth covered by the light during shadow rendering. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(65), DefaultValue(4), Limit(1, 4), EditorDisplay(\"Shadow\")")
|
||||
int32 CascadeCount = 4;
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
float FogDensity = 0.02f;
|
||||
|
||||
/// <summary>
|
||||
/// The fog height density factor that controls how the density increases as height decreases. The smaller values produce more visible transition larger.
|
||||
/// The fog height density factor that controls how the density increases as height decreases. Smaller values produce a more visible transition layer.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.2f), Limit(0.0001f, 10.0f, 0.001f), EditorDisplay(\"Exponential Height Fog\")")
|
||||
float FogHeightFalloff = 0.2f;
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
float StartDistance = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Scene elements past this distance will not have fog applied. This is useful for excluding skyboxes which already have fog baked in.
|
||||
/// Scene elements past this distance will not have fog applied. This is useful for excluding skyboxes which already have fog baked in. Setting this value to 0 disables it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(60), DefaultValue(0.0f), Limit(0), EditorDisplay(\"Exponential Height Fog\")")
|
||||
float FogCutoffDistance = 0.0f;
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
Color VolumetricFogAlbedo = Color::White;
|
||||
|
||||
/// <summary>
|
||||
/// Light emitted by height fog. This is a density so more light is emitted the further you are looking through the fog.
|
||||
/// Light emitted by height fog. This is a density value so more light is emitted the further you are looking through the fog.
|
||||
/// In most cases using a Skylight is a better choice, however, it may be useful in certain scenarios.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(330), DefaultValue(typeof(Color), \"0,0,0,1\"), EditorDisplay(\"Volumetric Fog\", \"Emissive\")")
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
float Brightness = 3.14f;
|
||||
|
||||
/// <summary>
|
||||
/// Controls light visibility range. The distance at which the light be completely faded. Use value 0 to always draw light.
|
||||
/// Controls light visibility range. The distance at which the light becomes completely faded. Use a value of 0 to always draw light.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(35), Limit(0, float.MaxValue, 10.0f), EditorDisplay(\"Light\")")
|
||||
float ViewDistance = 0.0f;
|
||||
@@ -87,19 +87,19 @@ public:
|
||||
float MinRoughness = 0.04f;
|
||||
|
||||
/// <summary>
|
||||
/// The light shadows casting distance from view.
|
||||
/// Shadows casting distance from view.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(80), EditorDisplay(\"Shadow\", \"Distance\"), Limit(0, 1000000)")
|
||||
float ShadowsDistance = 5000.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The light shadows fade off distance
|
||||
/// Shadows fade off distance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(90), EditorDisplay(\"Shadow\", \"Fade Distance\"), Limit(0.0f, 10000.0f, 0.1f)")
|
||||
float ShadowsFadeDistance = 500.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The light shadows edges sharpness
|
||||
/// TheShadows edges sharpness.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Shadow\", \"Sharpness\"), Limit(1.0f, 10.0f, 0.001f)")
|
||||
float ShadowsSharpness = 1.0f;
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
float ContactShadowsLength = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Shadows casting mode by this visual element
|
||||
/// Describes how a visual element casts shadows.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(60), EditorDisplay(\"Shadow\", \"Mode\")")
|
||||
ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
|
||||
|
||||
@@ -31,19 +31,19 @@ public:
|
||||
/// <summary>
|
||||
/// Directional light that is used to simulate the sun.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Sun\")")
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Sky\")")
|
||||
ScriptingObjectReference<DirectionalLight> SunLight;
|
||||
|
||||
/// <summary>
|
||||
/// The sun disc scale.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(2.0f), EditorDisplay(\"Sun\"), Limit(0, 100, 0.01f)")
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(2.0f), EditorDisplay(\"Sky\"), Limit(0, 100, 0.01f)")
|
||||
float SunDiscScale = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The sun power.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(30), DefaultValue(8.0f), EditorDisplay(\"Sun\"), Limit(0, 1000, 0.01f)")
|
||||
API_FIELD(Attributes="EditorOrder(30), DefaultValue(8.0f), EditorDisplay(\"Sky\"), Limit(0, 1000, 0.01f)")
|
||||
float SunPower = 8.0f;
|
||||
|
||||
private:
|
||||
|
||||
@@ -149,9 +149,14 @@ float Spline::GetSplineDuration() const
|
||||
float Spline::GetSplineLength() const
|
||||
{
|
||||
float sum = 0.0f;
|
||||
const int32 slices = 20;
|
||||
const float step = 1.0f / (float)slices;
|
||||
constexpr int32 slices = 20;
|
||||
constexpr float step = 1.0f / (float)slices;
|
||||
Vector3 prevPoint = Vector3::Zero;
|
||||
if (Curve.GetKeyframes().Count() != 0)
|
||||
{
|
||||
const auto& a = Curve[0];
|
||||
prevPoint = a.Value.Translation * _transform.Scale;
|
||||
}
|
||||
for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++)
|
||||
{
|
||||
const auto& a = Curve[i - 1];
|
||||
@@ -176,6 +181,37 @@ float Spline::GetSplineLength() const
|
||||
return Math::Sqrt(sum);
|
||||
}
|
||||
|
||||
float Spline::GetSplineSegmentLength(int32 index) const
|
||||
{
|
||||
if (index == 0)
|
||||
return 0.0f;
|
||||
CHECK_RETURN(index > 0 && index < GetSplinePointsCount(), 0.0f);
|
||||
float sum = 0.0f;
|
||||
constexpr int32 slices = 20;
|
||||
constexpr float step = 1.0f / (float)slices;
|
||||
const auto& a = Curve[index - 1];
|
||||
const auto& b = Curve[index];
|
||||
Vector3 startPoint = a.Value.Translation * _transform.Scale;
|
||||
{
|
||||
const float length = Math::Abs(b.Time - a.Time);
|
||||
Vector3 leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
||||
|
||||
// TODO: implement sth more analytical than brute-force solution
|
||||
for (int32 slice = 0; slice < slices; slice++)
|
||||
{
|
||||
const float t = (float)slice * step;
|
||||
Vector3 pos;
|
||||
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
||||
pos *= _transform.Scale;
|
||||
sum += (float)Vector3::DistanceSquared(pos, startPoint);
|
||||
startPoint = pos;
|
||||
}
|
||||
}
|
||||
return Math::Sqrt(sum);
|
||||
}
|
||||
|
||||
float Spline::GetSplineTime(int32 index) const
|
||||
{
|
||||
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), 0.0f)
|
||||
|
||||
@@ -167,6 +167,13 @@ public:
|
||||
/// </summary>
|
||||
API_PROPERTY() float GetSplineLength() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the spline segment (distance between pair of two points).
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the segment end index. Zero-based, smaller than GetSplinePointsCount().</param>
|
||||
/// <returns>The spline segment length.</returns>
|
||||
API_FUNCTION() float GetSplineSegmentLength(int32 index) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time of the spline keyframe.
|
||||
/// </summary>
|
||||
|
||||
@@ -24,7 +24,7 @@ private:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Light source bulb radius
|
||||
/// Light source bulb radius.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(2), DefaultValue(0.0f), EditorDisplay(\"Light\"), Limit(0, 1000, 0.01f)")
|
||||
float SourceRadius = 0.0f;
|
||||
@@ -42,54 +42,51 @@ public:
|
||||
float FallOffExponent = 8.0f;
|
||||
|
||||
/// <summary>
|
||||
/// IES texture (light profiles from real world measured data)
|
||||
/// IES texture (light profiles from real world measured data).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(211), DefaultValue(null), EditorDisplay(\"IES Profile\", \"IES Texture\")")
|
||||
AssetReference<IESProfile> IESTexture;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable using light brightness from IES profile
|
||||
/// Enable/disable using light brightness from IES profile.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(212), DefaultValue(false), EditorDisplay(\"IES Profile\", \"Use IES Brightness\")")
|
||||
bool UseIESBrightness = false;
|
||||
|
||||
/// <summary>
|
||||
/// Global scale for IES brightness contribution
|
||||
/// Global scale for IES brightness contribution.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(213), DefaultValue(1.0f), Limit(0, 10000, 0.01f), EditorDisplay(\"IES Profile\", \"Brightness Scale\")")
|
||||
float IESBrightnessScale = 1.0f;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Computes light brightness value
|
||||
/// Computes light brightness value.
|
||||
/// </summary>
|
||||
/// <returns>Brightness</returns>
|
||||
float ComputeBrightness() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets scaled light radius
|
||||
/// Gets scaled light radius.
|
||||
/// </summary>
|
||||
float GetScaledRadius() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets light radius
|
||||
/// Gets light radius.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(1000.0f), EditorDisplay(\"Light\"), Tooltip(\"Light radius\"), Limit(0, 10000, 0.1f)")
|
||||
API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(1000.0f), EditorDisplay(\"Light\"), Limit(0, 10000, 0.1f)")
|
||||
FORCE_INLINE float GetRadius() const
|
||||
{
|
||||
return _radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets light radius
|
||||
/// Sets light radius.
|
||||
/// </summary>
|
||||
/// <param name="value">New radius</param>
|
||||
API_PROPERTY() void SetRadius(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the spot light's outer cone angle (in degrees)
|
||||
/// Gets the spot light's outer cone angle (in degrees).
|
||||
/// </summary>
|
||||
/// <returns>Outer angle (in degrees)</returns>
|
||||
API_PROPERTY(Attributes="EditorOrder(22), DefaultValue(43.0f), EditorDisplay(\"Light\"), Limit(1, 89, 0.1f)")
|
||||
FORCE_INLINE float GetOuterConeAngle() const
|
||||
{
|
||||
@@ -97,15 +94,13 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the spot light's outer cone angle (in degrees)
|
||||
/// Sets the spot light's outer cone angle (in degrees).
|
||||
/// </summary>
|
||||
/// <param name="value">Value to assign</param>
|
||||
API_PROPERTY() void SetOuterConeAngle(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the spot light's inner cone angle (in degrees)
|
||||
/// Sets the spot light's inner cone angle (in degrees).
|
||||
/// </summary>
|
||||
/// <returns>Inner angle (in degrees)</returns>
|
||||
API_PROPERTY(Attributes="EditorOrder(21), DefaultValue(10.0f), EditorDisplay(\"Light\"), Limit(1, 89, 0.1f)")
|
||||
FORCE_INLINE float GetInnerConeAngle() const
|
||||
{
|
||||
@@ -113,9 +108,8 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the spot light's inner cone angle (in degrees)
|
||||
/// Sets the spot light's inner cone angle (in degrees).
|
||||
/// </summary>
|
||||
/// <param name="value">Value to assign</param>
|
||||
API_PROPERTY() void SetInnerConeAngle(float value);
|
||||
|
||||
private:
|
||||
|
||||
@@ -42,24 +42,7 @@ public:
|
||||
/// <summary>
|
||||
/// Field for assigning new script to transfer data to.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetReferenceScript(const ScriptingObjectReference<Script>& value)
|
||||
{
|
||||
_referenceScript = value;
|
||||
if (Data.IsEmpty())
|
||||
return;
|
||||
rapidjson_flax::Document document;
|
||||
document.Parse(Data.ToStringAnsi().GetText());
|
||||
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
_referenceScript->Deserialize(document, modifier.Value);
|
||||
|
||||
DeleteObject();
|
||||
}
|
||||
API_PROPERTY() void SetReferenceScript(const ScriptingObjectReference<Script>& value);
|
||||
};
|
||||
|
||||
inline MissingScript::MissingScript(const SpawnParams& params)
|
||||
: Script(params)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@ API_CLASS(Static) class FLAXENGINE_API LargeWorlds
|
||||
/// <summary>
|
||||
/// Defines the size of a single chunk. Large world (64-bit) gets divided into smaller chunks so all the math operations (32-bit) can be performed relative to the chunk origin without precision loss.
|
||||
/// </summary>
|
||||
API_FIELD() static constexpr Real ChunkSize = 262144;
|
||||
API_FIELD() static constexpr Real ChunkSize = 8192;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the large world origin to match the input position. The origin is snapped to the best matching chunk location.
|
||||
|
||||
@@ -416,7 +416,7 @@ public:
|
||||
}
|
||||
|
||||
// Load scene
|
||||
if (Level::loadScene(SceneAsset.Get()))
|
||||
if (Level::loadScene(SceneAsset))
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene {0}", SceneId);
|
||||
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, SceneId);
|
||||
@@ -816,40 +816,10 @@ bool LevelImpl::unloadScenes()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Level::loadScene(const Guid& sceneId)
|
||||
{
|
||||
const auto sceneAsset = Content::LoadAsync<JsonAsset>(sceneId);
|
||||
return loadScene(sceneAsset);
|
||||
}
|
||||
|
||||
bool Level::loadScene(const String& scenePath)
|
||||
{
|
||||
LOG(Info, "Loading scene from file. Path: \'{0}\'", scenePath);
|
||||
|
||||
// Check for missing file
|
||||
if (!FileSystem::FileExists(scenePath))
|
||||
{
|
||||
LOG(Error, "Missing scene file.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load file
|
||||
BytesContainer sceneData;
|
||||
if (File::ReadAllBytes(scenePath, sceneData))
|
||||
{
|
||||
LOG(Error, "Cannot load data from file.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return loadScene(sceneData);
|
||||
}
|
||||
|
||||
bool Level::loadScene(JsonAsset* sceneAsset)
|
||||
{
|
||||
// Keep reference to the asset (prevent unloading during action)
|
||||
AssetReference<JsonAsset> ref = sceneAsset;
|
||||
|
||||
// Wait for loaded
|
||||
if (sceneAsset == nullptr || sceneAsset->WaitForLoaded())
|
||||
{
|
||||
LOG(Error, "Cannot load scene asset.");
|
||||
@@ -879,6 +849,7 @@ bool Level::loadScene(const BytesContainer& sceneData, Scene** outScene)
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopeLock lock(ScenesLock);
|
||||
return loadScene(document, outScene);
|
||||
}
|
||||
|
||||
@@ -975,6 +946,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
SceneObject** objects = sceneObjects->Get();
|
||||
if (context.Async)
|
||||
{
|
||||
ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize)
|
||||
JobSystem::Execute([&](int32 i)
|
||||
{
|
||||
i++; // Start from 1. at index [0] was scene
|
||||
@@ -992,6 +964,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
else
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||
}, objectsCount - 1);
|
||||
ScenesLock.Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1091,6 +1064,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
}
|
||||
}
|
||||
}
|
||||
prefabSyncData.InitNewObjects();
|
||||
}
|
||||
|
||||
// /\ all above this has to be done on an any thread
|
||||
@@ -1135,7 +1109,7 @@ bool LevelImpl::saveScene(Scene* scene)
|
||||
|
||||
bool LevelImpl::saveScene(Scene* scene, const String& path)
|
||||
{
|
||||
ASSERT(scene);
|
||||
ASSERT(scene && EnumHasNoneFlags(scene->Flags, ObjectFlags::WasMarkedToDelete));
|
||||
auto sceneId = scene->GetID();
|
||||
|
||||
LOG(Info, "Saving scene {0} to \'{1}\'", scene->GetName(), path);
|
||||
@@ -1330,6 +1304,7 @@ bool Level::LoadScene(const Guid& id)
|
||||
}
|
||||
|
||||
// Load scene
|
||||
ScopeLock lock(ScenesLock);
|
||||
if (loadScene(sceneAsset))
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene {0}", id);
|
||||
|
||||
@@ -541,8 +541,8 @@ private:
|
||||
};
|
||||
|
||||
static void callActorEvent(ActorEventType eventType, Actor* a, Actor* b);
|
||||
static bool loadScene(const Guid& sceneId);
|
||||
static bool loadScene(const String& scenePath);
|
||||
|
||||
// All loadScene assume that ScenesLock has been taken by the calling thread
|
||||
static bool loadScene(JsonAsset* sceneAsset);
|
||||
static bool loadScene(const BytesContainer& sceneData, Scene** outScene = nullptr);
|
||||
static bool loadScene(rapidjson_flax::Document& document, Scene** outScene = nullptr);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user