Merge remote-tracking branch 'origin/master' into 1.13
# Conflicts: # Flax.flaxproj # Source/Engine/Level/Actors/StaticModel.cpp # Source/Engine/Level/Prefabs/Prefab.cpp # Source/Engine/Tools/ModelTool/ModelTool.cpp
This commit is contained in:
@@ -205,10 +205,9 @@ bool BehaviorKnowledge::Set(const StringAnsiView& path, const Variant& value)
|
||||
|
||||
bool BehaviorKnowledge::HasGoal(ScriptingTypeHandle type) const
|
||||
{
|
||||
for (int32 i = 0; i < Goals.Count(); i++)
|
||||
for (const Variant& goal : Goals)
|
||||
{
|
||||
const ScriptingTypeHandle goalType = Scripting::FindScriptingType(Goals[i].Type.GetTypeName());
|
||||
if (goalType == type)
|
||||
if (goal.Type.GetScriptingType() == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -218,8 +217,7 @@ const Variant& BehaviorKnowledge::GetGoal(ScriptingTypeHandle type) const
|
||||
{
|
||||
for (const Variant& goal : Goals)
|
||||
{
|
||||
const ScriptingTypeHandle goalType = Scripting::FindScriptingType(goal.Type.GetTypeName());
|
||||
if (goalType == type)
|
||||
if (goal.Type.GetScriptingType() == type)
|
||||
return goal;
|
||||
}
|
||||
return Variant::Null;
|
||||
@@ -242,10 +240,9 @@ void BehaviorKnowledge::RemoveGoal(ScriptingTypeHandle type)
|
||||
{
|
||||
for (int32 i = 0; i < Goals.Count(); i++)
|
||||
{
|
||||
const ScriptingTypeHandle goalType = Scripting::FindScriptingType(Goals[i].Type.GetTypeName());
|
||||
if (goalType == type)
|
||||
if (Goals[i].Type.GetScriptingType() == type)
|
||||
{
|
||||
Goals.RemoveAt(i);
|
||||
Goals.RemoveAtKeepOrder(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,14 +61,14 @@ void AnimGraphBase::Clear()
|
||||
StateTransitions.Resize(0);
|
||||
|
||||
// Base
|
||||
GraphType::Clear();
|
||||
VisjectGraph::Clear();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void AnimGraphBase::GetReferences(Array<Guid>& output) const
|
||||
{
|
||||
GraphType::GetReferences(output);
|
||||
VisjectGraph::GetReferences(output);
|
||||
|
||||
// Collect references from nested graph (assets used in state machines)
|
||||
for (const auto* subGraph : SubGraphs)
|
||||
|
||||
@@ -873,65 +873,41 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu
|
||||
switch (param->Type.Type)
|
||||
{
|
||||
case VariantType::Float2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsFloat2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Float3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsFloat3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Float4:
|
||||
case VariantType::Color:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsFloat4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsDouble2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsDouble3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double4:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsDouble4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Int2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsInt2().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Int3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsInt3().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Int4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsInt4().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Matrix:
|
||||
{
|
||||
|
||||
@@ -272,6 +272,7 @@ String Asset::ToString() const
|
||||
|
||||
void Asset::OnDeleteObject()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Asset.Unload");
|
||||
ASSERT(IsInMainThread());
|
||||
|
||||
// Send event to the gameplay so it can release handle to this asset
|
||||
|
||||
@@ -163,7 +163,7 @@ VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box)
|
||||
|
||||
// Add to the calling stack
|
||||
VisualScripting::StackFrame frame = *stack.Stack;
|
||||
frame.Node = parentNode;
|
||||
frame.Node = (VisualScriptGraphNode*)parentNode;
|
||||
frame.Box = box;
|
||||
frame.PreviousFrame = stack.Stack;
|
||||
stack.Stack = &frame;
|
||||
@@ -189,7 +189,7 @@ VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box)
|
||||
VisjectExecutor::Graph* VisualScriptExecutor::GetCurrentGraph() const
|
||||
{
|
||||
auto& stack = ThreadStacks.Get();
|
||||
return stack.Stack && stack.Stack->Script ? &stack.Stack->Script->Graph : nullptr;
|
||||
return stack.Stack && stack.Stack->Script ? (Graph*)&stack.Stack->Script->Graph : nullptr;
|
||||
}
|
||||
|
||||
void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value)
|
||||
@@ -339,7 +339,7 @@ void VisualScriptExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
obj = Value::Null;
|
||||
#else
|
||||
const ScriptingTypeHandle type = Scripting::FindScriptingType(StringAnsiView(typeNameAnsi.Get(), typeName.Length()));
|
||||
const ScriptingTypeHandle objType = Scripting::FindScriptingType(obj.Type.GetTypeName());
|
||||
const ScriptingTypeHandle objType = obj.Type.GetScriptingType();
|
||||
if (!type || !objType || !objType.IsSubclassOf(type))
|
||||
obj = Value::Null;
|
||||
#endif
|
||||
@@ -432,7 +432,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
// Call Impulse or Pure Method
|
||||
if (boxBase->ID == 0 || (bool)node->Values[3])
|
||||
{
|
||||
auto& cache = node->Data.InvokeMethod;
|
||||
auto& cache = ((VisualScriptGraphNode*)node)->Data.InvokeMethod;
|
||||
if (!cache.Method)
|
||||
{
|
||||
// Load method signature
|
||||
@@ -667,7 +667,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
// Get Field
|
||||
case 7:
|
||||
{
|
||||
auto& cache = node->Data.GetSetField;
|
||||
auto& cache = ((VisualScriptGraphNode*)node)->Data.GetSetField;
|
||||
if (!cache.Field)
|
||||
{
|
||||
const auto typeName = (StringView)node->Values[0];
|
||||
@@ -753,7 +753,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
// Get Field
|
||||
case 8:
|
||||
{
|
||||
auto& cache = node->Data.GetSetField;
|
||||
auto& cache = ((VisualScriptGraphNode*)node)->Data.GetSetField;
|
||||
if (!cache.Field)
|
||||
{
|
||||
const auto typeName = (StringView)node->Values[0];
|
||||
|
||||
@@ -12,11 +12,47 @@
|
||||
#define VISUAL_SCRIPT_GRAPH_MAX_CALL_STACK 250
|
||||
#define VISUAL_SCRIPT_DEBUGGING USE_EDITOR
|
||||
|
||||
#define VisualScriptGraphNode VisjectGraphNode<>
|
||||
|
||||
class VisualScripting;
|
||||
class VisualScriptingBinaryModule;
|
||||
|
||||
/// <summary>
|
||||
/// Visual Script graph node.
|
||||
/// </summary>
|
||||
class VisualScriptGraphNode : public VisjectGraphNode<>
|
||||
{
|
||||
public:
|
||||
struct InvokeMethodData
|
||||
{
|
||||
void* Method;
|
||||
BinaryModule* Module;
|
||||
int32 ParamsCount;
|
||||
uint32 OutParamsMask;
|
||||
bool IsStatic;
|
||||
};
|
||||
|
||||
struct GetSetFieldData
|
||||
{
|
||||
void* Field;
|
||||
BinaryModule* Module;
|
||||
bool IsStatic;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Custom cached data per node type. Compact to use as small amount of memory as possible.
|
||||
/// </summary>
|
||||
struct AdditionalData
|
||||
{
|
||||
union
|
||||
{
|
||||
InvokeMethodData InvokeMethod;
|
||||
GetSetFieldData GetSetField;
|
||||
};
|
||||
};
|
||||
|
||||
// The custom per-node data. Used to cache data for faster usage at runtime.
|
||||
AdditionalData Data;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The Visual Script graph data.
|
||||
/// </summary>
|
||||
|
||||
@@ -564,10 +564,7 @@ ContentLoadTask* BinaryAsset::createLoadingTask()
|
||||
loadTask = preLoadChunksTask;
|
||||
}
|
||||
|
||||
// Before asset loading we have to initialize storage
|
||||
// TODO: maybe in build game we could do it in place?
|
||||
// This step is only for opening asset files in background and upgrading them
|
||||
// In build game we have only a few packages which are ready to use
|
||||
// Before asset loading we have to initialize storage and pull the asset header
|
||||
auto initTask = New<InitAssetTask>(this);
|
||||
initTask->ContinueWith(loadTask);
|
||||
loadTask = initTask;
|
||||
|
||||
@@ -113,7 +113,7 @@ void AssetsCache::Init()
|
||||
}
|
||||
|
||||
// Use only valid entries
|
||||
if (IsEntryValid(e))
|
||||
if (IsEntryValid(e) != EntryValidation::Invalid)
|
||||
_registry.Add(e.Info.ID, e);
|
||||
else
|
||||
rejectedCount++;
|
||||
@@ -295,14 +295,23 @@ bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
|
||||
auto& e = i->Value;
|
||||
if (e.Info.Path == path)
|
||||
{
|
||||
if (!IsEntryValid(e))
|
||||
const auto validation = IsEntryValid(e);
|
||||
if (validation == EntryValidation::Invalid)
|
||||
{
|
||||
LOG(Warning, "Missing file from registry: \'{0}\':{1}:{2}", e.Info.Path, e.Info.ID, e.Info.TypeName);
|
||||
_registry.Remove(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
if (validation == EntryValidation::Inaccessible && !e.WarnedInaccessible)
|
||||
{
|
||||
e.WarnedInaccessible = true;
|
||||
LOG(Warning, "Asset file locked, keeping cached entry: \'{0}\':{1}:{2}", e.Info.Path, e.Info.ID, e.Info.TypeName);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Found valid or inaccessible but return cached info either way
|
||||
result = true;
|
||||
info = e.Info;
|
||||
}
|
||||
@@ -322,13 +331,22 @@ bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
|
||||
auto e = _registry.TryGet(id);
|
||||
if (e != nullptr)
|
||||
{
|
||||
if (!IsEntryValid(*e))
|
||||
const auto validation = IsEntryValid(*e);
|
||||
if (validation == EntryValidation::Invalid)
|
||||
{
|
||||
LOG(Warning, "Missing file from registry: \'{0}\':{1}:{2}", e->Info.Path, e->Info.ID, e->Info.TypeName);
|
||||
_registry.Remove(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
if (validation == EntryValidation::Inaccessible && !e->WarnedInaccessible)
|
||||
{
|
||||
e->WarnedInaccessible = true;
|
||||
LOG(Warning, "Asset file locked, keeping cached entry: \'{0}\':{1}:{2}", e->Info.Path, e->Info.ID, e->Info.TypeName);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Found
|
||||
result = true;
|
||||
info = e->Info;
|
||||
@@ -360,13 +378,13 @@ void AssetsCache::GetAllByTypeName(const StringView& typeName, Array<Guid>& resu
|
||||
void AssetsCache::RegisterAssets(FlaxStorage* storage)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
ASSERT(storage);
|
||||
|
||||
// Get all entries
|
||||
Array<FlaxStorage::Entry> entries;
|
||||
storage->GetEntries(entries);
|
||||
ASSERT(entries.HasItems());
|
||||
if (entries.IsEmpty())
|
||||
return;
|
||||
|
||||
ASSETS_CACHE_LOCK();
|
||||
auto storagePath = storage->GetPath();
|
||||
@@ -567,60 +585,70 @@ bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPa
|
||||
|
||||
#endif
|
||||
|
||||
bool AssetsCache::IsEntryValid(Entry& e)
|
||||
AssetsCache::EntryValidation AssetsCache::IsEntryValid(Entry& e)
|
||||
{
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
// Check if file exists
|
||||
if (FileSystem::FileExists(e.Info.Path))
|
||||
if (!FileSystem::FileExists(e.Info.Path))
|
||||
return EntryValidation::Invalid;
|
||||
|
||||
// Check if file hasn't been modified
|
||||
const auto fileModified = FileSystem::GetFileLastEditTime(e.Info.Path);
|
||||
if (fileModified == e.FileModified)
|
||||
{
|
||||
// Check if file hasn't been modified
|
||||
const auto fileModified = FileSystem::GetFileLastEditTime(e.Info.Path);
|
||||
if (fileModified == e.FileModified)
|
||||
return true;
|
||||
|
||||
const auto extension = FileSystem::GetExtension(e.Info.Path).ToLower();
|
||||
|
||||
// Check if it's a binary asset
|
||||
if (ContentStorageManager::IsFlaxStorageExtension(extension))
|
||||
{
|
||||
// Validate ID within storage container
|
||||
const auto storage = ContentStorageManager::GetStorage(e.Info.Path);
|
||||
if (storage)
|
||||
{
|
||||
// Check if storage at given location contains that asset
|
||||
const bool isValid = storage->HasAsset(e.Info);
|
||||
|
||||
// Update entry and mark cache as dirty
|
||||
e.FileModified = fileModified;
|
||||
_isDirty = true;
|
||||
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
// Check for json resource
|
||||
else if (JsonStorageProxy::IsValidExtension(extension))
|
||||
{
|
||||
// Check Json storage layer
|
||||
Guid jsonId;
|
||||
String jsonTypeName;
|
||||
if (JsonStorageProxy::GetAssetInfo(e.Info.Path, jsonId, jsonTypeName))
|
||||
{
|
||||
const bool isValid = e.Info.ID == jsonId && e.Info.TypeName == jsonTypeName;
|
||||
|
||||
// Update entry and mark cache as dirty
|
||||
e.FileModified = fileModified;
|
||||
_isDirty = true;
|
||||
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
e.WarnedInaccessible = false;
|
||||
return EntryValidation::Valid;
|
||||
}
|
||||
|
||||
return false;
|
||||
const auto extension = FileSystem::GetExtension(e.Info.Path).ToLower();
|
||||
|
||||
// Check if it's a binary asset
|
||||
if (ContentStorageManager::IsFlaxStorageExtension(extension))
|
||||
{
|
||||
// Validate ID within storage container
|
||||
const auto storage = ContentStorageManager::GetStorage(e.Info.Path);
|
||||
if (storage)
|
||||
{
|
||||
// Check if storage at given location contains that asset
|
||||
const bool isValid = storage->HasAsset(e.Info);
|
||||
|
||||
// Update entry and mark cache as dirty
|
||||
e.FileModified = fileModified;
|
||||
e.WarnedInaccessible = false;
|
||||
_isDirty = true;
|
||||
|
||||
return isValid ? EntryValidation::Valid : EntryValidation::Invalid;
|
||||
}
|
||||
}
|
||||
// Check for json resource
|
||||
else if (JsonStorageProxy::IsValidExtension(extension))
|
||||
{
|
||||
// Check Json storage layer
|
||||
Guid jsonId;
|
||||
String jsonTypeName;
|
||||
if (JsonStorageProxy::GetAssetInfo(e.Info.Path, jsonId, jsonTypeName))
|
||||
{
|
||||
const bool isValid = e.Info.ID == jsonId && e.Info.TypeName == jsonTypeName;
|
||||
|
||||
// Update entry and mark cache as dirty
|
||||
e.FileModified = fileModified;
|
||||
e.WarnedInaccessible = false;
|
||||
_isDirty = true;
|
||||
|
||||
return isValid ? EntryValidation::Valid : EntryValidation::Invalid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown file type
|
||||
return EntryValidation::Invalid;
|
||||
}
|
||||
|
||||
// File exists but cannot be read (likely locked by git or another process)
|
||||
return EntryValidation::Inaccessible;
|
||||
#else
|
||||
// In game we don't care about it because all cached asset entries are valid (precached)
|
||||
// Skip only entries with missing file
|
||||
return e.Info.Path.HasChars();
|
||||
return e.Info.Path.HasChars() ? EntryValidation::Valid : EntryValidation::Invalid;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -58,6 +58,11 @@ public:
|
||||
/// The file modified date.
|
||||
/// </summary>
|
||||
DateTime FileModified;
|
||||
|
||||
/// <summary>
|
||||
/// True if a warning about this entry being inaccessible has already been logged (prevents log spam). Runtime-only, not serialized.
|
||||
/// </summary>
|
||||
bool WarnedInaccessible = false;
|
||||
#endif
|
||||
|
||||
Entry()
|
||||
@@ -73,6 +78,27 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Result of validating an asset cache entry.
|
||||
/// </summary>
|
||||
enum class EntryValidation
|
||||
{
|
||||
/// <summary>
|
||||
/// File verified, contains this asset.
|
||||
/// </summary>
|
||||
Valid,
|
||||
|
||||
/// <summary>
|
||||
/// File missing or contains a different asset.
|
||||
/// </summary>
|
||||
Invalid,
|
||||
|
||||
/// <summary>
|
||||
/// File exists but cannot be opened (locked by another process).
|
||||
/// </summary>
|
||||
Inaccessible,
|
||||
};
|
||||
|
||||
typedef Dictionary<Guid, Entry> Registry;
|
||||
typedef Dictionary<String, Guid> PathsMapping;
|
||||
|
||||
@@ -232,6 +258,6 @@ public:
|
||||
/// Determines whether cached asset entry is valid.
|
||||
/// </summary>
|
||||
/// <param name="e">The asset entry.</param>
|
||||
/// <returns>True if is valid, otherwise false.</returns>
|
||||
bool IsEntryValid(Entry& e);
|
||||
/// <returns>The validation result.</returns>
|
||||
EntryValidation IsEntryValid(Entry& e);
|
||||
};
|
||||
|
||||
@@ -54,6 +54,7 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
|
||||
Locker.Lock();
|
||||
|
||||
// Try fast lookup
|
||||
bool wasCached = true;
|
||||
FlaxStorage* storage;
|
||||
if (!StorageMap.TryGet(path, storage))
|
||||
{
|
||||
@@ -74,6 +75,7 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
|
||||
|
||||
// Register storage container
|
||||
StorageMap.Add(path, storage);
|
||||
wasCached = false;
|
||||
}
|
||||
|
||||
// Build reference (before releasing the lock so ContentStorageSystem::Job won't delete it when running from async thread)
|
||||
@@ -90,6 +92,8 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
|
||||
if (loadFailed)
|
||||
{
|
||||
LOG(Error, "Failed to load {0}.", path);
|
||||
if (wasCached)
|
||||
return result;
|
||||
Locker.Lock();
|
||||
StorageMap.Remove(path);
|
||||
if (storage->IsPackage())
|
||||
|
||||
@@ -243,9 +243,9 @@ FlaxStorage::~FlaxStorage()
|
||||
{
|
||||
// Validate if has been disposed
|
||||
ASSERT(IsDisposed());
|
||||
CHECK(_chunksLock == 0);
|
||||
CHECK(_refCount == 0);
|
||||
CHECK(_isUnloadingData == 0);
|
||||
CHECK_NO_RETURN(_chunksLock == 0);
|
||||
CHECK_NO_RETURN(_refCount == 0);
|
||||
CHECK_NO_RETURN(_isUnloadingData == 0);
|
||||
ASSERT(_chunks.IsEmpty());
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
// Float
|
||||
|
||||
static_assert(sizeof(Float2) == 8, "Invalid Float2 type size.");
|
||||
static_assert(sizeof(Int2) == 8, "Invalid Int2 type size.");
|
||||
static_assert(sizeof(Double2) == 16, "Invalid Double2 type size.");
|
||||
|
||||
template<>
|
||||
const Float2 Float2::Zero(0.0f);
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
// Float
|
||||
|
||||
static_assert(sizeof(Float3) == 12, "Invalid Float3 type size.");
|
||||
static_assert(sizeof(Int3) == 12, "Invalid Int3 type size.");
|
||||
static_assert(sizeof(Double3) == 24, "Invalid Double3 type size.");
|
||||
|
||||
template<>
|
||||
const Float3 Float3::Zero(0.0f);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
// Float
|
||||
|
||||
static_assert(sizeof(Float4) == 16, "Invalid Float4 type size.");
|
||||
static_assert(sizeof(Int4) == 16, "Invalid Int4 type size.");
|
||||
static_assert(sizeof(Double4) == 32, "Invalid Double4 type size.");
|
||||
|
||||
template<>
|
||||
const Float4 Float4::Zero(0.0f);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Engine/Core/Collections/HashFunctions.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Mathd.h"
|
||||
#include "Engine/Core/Math/BoundingBox.h"
|
||||
@@ -120,7 +121,7 @@ VariantType::VariantType(Types type, const StringAnsiView& typeName, bool static
|
||||
VariantType::VariantType(Types type, const ScriptingType& sType)
|
||||
: VariantType(type)
|
||||
{
|
||||
SetTypeName(sType.Fullname, sType.Module->CanReload);
|
||||
SetTypeName(sType.Fullname, !sType.Module->CanReload);
|
||||
}
|
||||
|
||||
VariantType::VariantType(Types type, const MClass* klass)
|
||||
@@ -340,13 +341,13 @@ void VariantType::SetTypeName(const StringAnsiView& typeName, bool staticName)
|
||||
|
||||
void VariantType::SetTypeName(const ScriptingType& type)
|
||||
{
|
||||
SetTypeName(type.Fullname, type.Module->CanReload);
|
||||
SetTypeName(type.Fullname, !type.Module->CanReload);
|
||||
}
|
||||
|
||||
void VariantType::SetTypeName(const MClass& klass)
|
||||
{
|
||||
#if USE_CSHARP
|
||||
SetTypeName(klass.GetFullName(), klass.GetAssembly()->CanReload());
|
||||
SetTypeName(klass.GetFullName(), !klass.GetAssembly()->CanReload());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -357,6 +358,11 @@ const char* VariantType::GetTypeName() const
|
||||
return InBuiltTypesTypeNames[Type];
|
||||
}
|
||||
|
||||
ScriptingTypeHandle VariantType::GetScriptingType() const
|
||||
{
|
||||
return Scripting::FindScriptingType(GetTypeName());
|
||||
}
|
||||
|
||||
VariantType VariantType::GetElementType() const
|
||||
{
|
||||
if (Type == Array)
|
||||
@@ -3189,6 +3195,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3212,6 +3221,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3235,6 +3247,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3258,6 +3273,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3281,6 +3299,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3304,6 +3325,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3327,6 +3351,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3350,6 +3377,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3373,6 +3403,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Double2:
|
||||
case VariantType::Double3:
|
||||
case VariantType::Double4:
|
||||
case VariantType::Int2:
|
||||
case VariantType::Int3:
|
||||
case VariantType::Int4:
|
||||
case VariantType::Enum:
|
||||
return true;
|
||||
default:
|
||||
@@ -3516,6 +3549,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3(v.AsBool ? 1.0 : 0.0));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4(v.AsBool ? 1.0 : 0.0));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2(v.AsBool ? 1 : 0));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3(v.AsBool ? 1 : 0));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4(v.AsBool ? 1 : 0));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, v.AsBool ? 1 : 0);
|
||||
default: ;
|
||||
@@ -3554,6 +3593,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3((double)v.AsInt16));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsInt16));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2(v.AsInt16));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3(v.AsInt16));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4(v.AsInt16));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsInt16);
|
||||
default: ;
|
||||
@@ -3584,6 +3629,18 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Float3((float)v.AsInt));
|
||||
case VariantType::Float4:
|
||||
return Variant(Float4((float)v.AsInt));
|
||||
case VariantType::Double2:
|
||||
return Variant(Double2((double)v.AsInt));
|
||||
case VariantType::Double3:
|
||||
return Variant(Double3((double)v.AsInt));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsInt));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2(v.AsInt));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3(v.AsInt));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4(v.AsInt));
|
||||
case VariantType::Color:
|
||||
return Variant(Color((float)v.AsInt));
|
||||
case VariantType::Enum:
|
||||
@@ -3624,6 +3681,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3((double)v.AsUint16));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsUint16));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2(v.AsUint16));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3(v.AsUint16));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4(v.AsUint16));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsUint16);
|
||||
default: ;
|
||||
@@ -3662,6 +3725,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3((double)v.AsUint));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsUint));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsUint));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsUint));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsUint));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsUint);
|
||||
default: ;
|
||||
@@ -3700,6 +3769,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3((double)v.AsInt64));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsInt64));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsInt64));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsInt64));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsInt64));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsInt64);
|
||||
default: ;
|
||||
@@ -3719,25 +3794,31 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
case VariantType::Uint16:
|
||||
return Variant((uint16)v.AsUint16);
|
||||
case VariantType::Uint:
|
||||
return Variant((uint32)v.AsUint);
|
||||
return Variant((uint32)v.AsUint64);
|
||||
case VariantType::Float:
|
||||
return Variant((float)v.AsUint64);
|
||||
case VariantType::Double:
|
||||
return Variant((double)v.AsUint64);
|
||||
case VariantType::Float2:
|
||||
return Variant(Float2((float)v.AsInt));
|
||||
return Variant(Float2((float)v.AsUint64));
|
||||
case VariantType::Float3:
|
||||
return Variant(Float3((float)v.AsInt));
|
||||
return Variant(Float3((float)v.AsUint64));
|
||||
case VariantType::Float4:
|
||||
return Variant(Float4((float)v.AsInt));
|
||||
return Variant(Float4((float)v.AsUint64));
|
||||
case VariantType::Color:
|
||||
return Variant(Color((float)v.AsInt));
|
||||
return Variant(Color((float)v.AsUint64));
|
||||
case VariantType::Double2:
|
||||
return Variant(Double2((double)v.AsInt));
|
||||
return Variant(Double2((double)v.AsUint64));
|
||||
case VariantType::Double3:
|
||||
return Variant(Double3((double)v.AsInt));
|
||||
return Variant(Double3((double)v.AsUint64));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4((double)v.AsInt));
|
||||
return Variant(Double4((double)v.AsUint64));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsUint64));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsUint64));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsUint64));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsInt);
|
||||
default: ;
|
||||
@@ -3776,6 +3857,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3(v.AsFloat));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4(v.AsFloat));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsFloat));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsFloat));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsFloat));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsFloat);
|
||||
default: ;
|
||||
@@ -3814,6 +3901,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
|
||||
return Variant(Double3(v.AsDouble));
|
||||
case VariantType::Double4:
|
||||
return Variant(Double4(v.AsDouble));
|
||||
case VariantType::Int2:
|
||||
return Variant(Int2((int32)v.AsFloat));
|
||||
case VariantType::Int3:
|
||||
return Variant(Int3((int32)v.AsFloat));
|
||||
case VariantType::Int4:
|
||||
return Variant(Int4((int32)v.AsFloat));
|
||||
case VariantType::Enum:
|
||||
return Enum(to, (int64)v.AsDouble);
|
||||
default: ;
|
||||
@@ -4242,6 +4335,7 @@ void Variant::AllocStructure()
|
||||
AsBlob.Length = 2;
|
||||
AsBlob.Data = Allocator::Allocate(AsBlob.Length);
|
||||
*((int16*)AsBlob.Data) = 0;
|
||||
MARK_CONTENT_DEPRECATED();
|
||||
}
|
||||
#if USE_CSHARP
|
||||
else if (const auto mclass = Scripting::FindClass(typeName))
|
||||
@@ -4367,6 +4461,13 @@ uint32 GetHash(const Variant& key)
|
||||
return GetHash((void*)key.AsObject);
|
||||
case VariantType::Structure:
|
||||
case VariantType::Blob:
|
||||
case VariantType::Transform:
|
||||
case VariantType::Matrix:
|
||||
#if USE_LARGE_WORLDS
|
||||
case VariantType::BoundingSphere:
|
||||
case VariantType::BoundingBox:
|
||||
case VariantType::Ray:
|
||||
#endif
|
||||
return Crc::MemCrc32(key.AsBlob.Data, key.AsBlob.Length);
|
||||
case VariantType::Asset:
|
||||
return GetHash((void*)key.AsAsset);
|
||||
@@ -4376,6 +4477,24 @@ uint32 GetHash(const Variant& key)
|
||||
return GetHash(*(Guid*)key.AsData);
|
||||
case VariantType::Typename:
|
||||
return GetHash((const char*)key.AsBlob.Data);
|
||||
case VariantType::Float2:
|
||||
return GetHash(*(const Float2*)key.AsData);
|
||||
case VariantType::Float3:
|
||||
return GetHash(*(const Float3*)key.AsData);
|
||||
case VariantType::Float4:
|
||||
return GetHash(*(const Float4*)key.AsData);
|
||||
case VariantType::Int2:
|
||||
return GetHash(*(const Int2*)key.AsData);
|
||||
case VariantType::Int3:
|
||||
return GetHash(*(const Int3*)key.AsData);
|
||||
case VariantType::Int4:
|
||||
return GetHash(*(const Int4*)key.AsData);
|
||||
case VariantType::Double2:
|
||||
return GetHash(*(const Double2*)key.AsData);
|
||||
case VariantType::Double3:
|
||||
return GetHash(*(const Double3*)key.AsData);
|
||||
case VariantType::Double4:
|
||||
return GetHash(*(const Double4*)key.AsBlob.Data);
|
||||
case VariantType::ManagedObject:
|
||||
#if USE_CSHARP
|
||||
return key.MANAGED_GC_HANDLE ? (uint32)MCore::Object::GetHashCode(MCore::GCHandle::GetTarget(key.MANAGED_GC_HANDLE)) : 0;
|
||||
|
||||
@@ -151,6 +151,7 @@ public:
|
||||
void SetTypeName(const ScriptingType& type);
|
||||
void SetTypeName(const MClass& klass);
|
||||
const char* GetTypeName() const;
|
||||
ScriptingTypeHandle GetScriptingType() const;
|
||||
VariantType GetElementType() const;
|
||||
// Drops custom type name into the name allocated by the scripting module to reduce memory allocations when referencing types.
|
||||
void Inline();
|
||||
@@ -420,6 +421,15 @@ public:
|
||||
return MoveTemp(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static typename TEnableIf<TIsEnum<T>::Value, Variant>::Type Enum(const T value)
|
||||
{
|
||||
Variant v;
|
||||
v.SetType(VariantType(VariantType::Enum, StaticType<T>().GetType()));
|
||||
v.AsUint64 = (uint64)value;
|
||||
return MoveTemp(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static typename TEnableIf<!TIsEnum<T>::Value && !TIsPointer<T>::Value, Variant>::Type Structure(VariantType&& type, const T& value)
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@ struct CommandData
|
||||
else if (value.Type.Type == VariantType::Structure)
|
||||
{
|
||||
// Prettify structure printing
|
||||
ScriptingTypeHandle resultType = Scripting::FindScriptingType(value.Type.GetTypeName());
|
||||
ScriptingTypeHandle resultType = value.Type.GetScriptingType();
|
||||
if (resultType)
|
||||
{
|
||||
Array<void*> fields;
|
||||
|
||||
@@ -76,9 +76,10 @@ void GPUTasksContext::OnFrameBegin()
|
||||
{
|
||||
auto task = _tasksSyncing[i];
|
||||
auto state = task->GetState();
|
||||
if (EnumHasAllFlags(task->Flags, ObjectFlags::WasMarkedToDelete))
|
||||
state = TaskState::Finished;
|
||||
if (task->GetSyncPoint() <= _currentSyncPoint && state != TaskState::Finished)
|
||||
{
|
||||
// TODO: add stats counter and count performed jobs, print to log on exit.
|
||||
task->Sync();
|
||||
}
|
||||
if (state == TaskState::Failed || state == TaskState::Canceled)
|
||||
|
||||
@@ -460,6 +460,18 @@ void MaterialParameter::Bind(BindMeta& meta) const
|
||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
|
||||
*((Float4*)(meta.Constants.Get() + _offset)) = (Float4)e->Value.AsDouble4();
|
||||
break;
|
||||
case VariantType::Int2:
|
||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Int2)));
|
||||
*((Int2*)(meta.Constants.Get() + _offset)) = (Int2)e->Value.AsInt2();
|
||||
break;
|
||||
case VariantType::Int3:
|
||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Int3)));
|
||||
*((Int3*)(meta.Constants.Get() + _offset)) = (Int3)e->Value.AsInt3();
|
||||
break;
|
||||
case VariantType::Int4:
|
||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Int4)));
|
||||
*((Int4*)(meta.Constants.Get() + _offset)) = (Int4)e->Value.AsInt4();
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,7 +621,7 @@ namespace FlaxEngine
|
||||
{
|
||||
ibData = dataPtr[IB];
|
||||
use16BitIndexBuffer = _formats[IB] == PixelFormat.R16_UInt;
|
||||
triangles = (uint)(_data[IB].Length / PixelFormatExtensions.SizeInBytes(_formats[IB]));
|
||||
triangles = (uint)(_data[IB].Length / (PixelFormatExtensions.SizeInBytes(_formats[IB]) * 3));
|
||||
}
|
||||
|
||||
if (mesh.Init(vertices, triangles, vbData, ibData, use16BitIndexBuffer, vbLayout))
|
||||
@@ -643,11 +643,16 @@ namespace FlaxEngine
|
||||
else
|
||||
{
|
||||
Float3 min = Float3.Maximum, max = Float3.Minimum;
|
||||
for (int i = 0; i < vertices; i++)
|
||||
PixelFormatSampler.Get(positionStream.Format, out var positionSampler);
|
||||
int positionStride = positionStream.Stride;
|
||||
fixed (byte* data = positionStream.Data)
|
||||
{
|
||||
Float3 pos = positionStream.GetFloat3(i);
|
||||
Float3.Min(ref min, ref pos, out min);
|
||||
Float3.Max(ref max, ref pos, out max);
|
||||
for (int i = 0; i < vertices; i++)
|
||||
{
|
||||
Float3 pos = new Float3(positionSampler.Read(data + i * positionStride));
|
||||
Float3.Min(ref min, ref pos, out min);
|
||||
Float3.Max(ref max, ref pos, out max);
|
||||
}
|
||||
}
|
||||
bounds = new BoundingBox(min, max);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,13 @@ bool ModelInstanceEntries::HasContentLoaded() const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModelInstanceEntries::ShouldSerialize(const void* otherObj) const
|
||||
{
|
||||
if (!otherObj)
|
||||
return true;
|
||||
return !(*this == *(const ModelInstanceEntries*)otherObj);
|
||||
}
|
||||
|
||||
void ModelInstanceEntries::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
SERIALIZE_GET_OTHER_OBJ(ModelInstanceEntries);
|
||||
@@ -43,12 +50,13 @@ void ModelInstanceEntries::Serialize(SerializeStream& stream, const void* otherO
|
||||
void ModelInstanceEntries::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
{
|
||||
PROFILE_MEM(Graphics);
|
||||
const DeserializeStream& entries = stream["Entries"];
|
||||
ASSERT(entries.IsArray());
|
||||
Resize(entries.Size());
|
||||
for (rapidjson::SizeType i = 0; i < entries.Size(); i++)
|
||||
const DeserializeStream& entriesData = stream[rapidjson_flax::Value(rapidjson::StringRef("Entries", 7))];
|
||||
CHECK(entriesData.IsArray());
|
||||
Resize(entriesData.Size());
|
||||
ModelInstanceEntry* entries = Get();
|
||||
for (int32 i = 0; i < Count(); i++)
|
||||
{
|
||||
At(i).Deserialize((DeserializeStream&)entries[i], modifier);
|
||||
entries[i].Deserialize((DeserializeStream&)entriesData[i], modifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ public:
|
||||
|
||||
public:
|
||||
// [ISerializable]
|
||||
bool ShouldSerialize(const void* otherObj) const override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
};
|
||||
|
||||
@@ -353,8 +353,11 @@ Viewport SceneRenderTask::GetViewport() const
|
||||
viewport = Buffers->GetViewport();
|
||||
else
|
||||
viewport = Viewport(0, 0, 1280, 720);
|
||||
viewport.Width *= RenderingPercentage;
|
||||
viewport.Height *= RenderingPercentage;
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
float renderScale = RenderingPercentage * RenderScale;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
viewport.Width *= renderScale;
|
||||
viewport.Height *= renderScale;
|
||||
return viewport;
|
||||
}
|
||||
|
||||
@@ -394,13 +397,16 @@ void SceneRenderTask::OnBegin(GPUContext* context)
|
||||
}
|
||||
|
||||
// Setup render buffers for the output rendering resolution
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
float renderScale = RenderingPercentage * RenderScale;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
if (Output)
|
||||
{
|
||||
Buffers->Init((int32)((float)Output->Width() * RenderingPercentage), (int32)((float)Output->Height() * RenderingPercentage));
|
||||
Buffers->Init((int32)((float)Output->Width() * renderScale), (int32)((float)Output->Height() * renderScale));
|
||||
}
|
||||
else if (SwapChain)
|
||||
{
|
||||
Buffers->Init((int32)((float)SwapChain->GetWidth() * RenderingPercentage), (int32)((float)SwapChain->GetHeight() * RenderingPercentage));
|
||||
Buffers->Init((int32)((float)SwapChain->GetWidth() * renderScale), (int32)((float)SwapChain->GetHeight() * renderScale));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,7 +440,10 @@ bool SceneRenderTask::Resize(int32 width, int32 height)
|
||||
PROFILE_MEM(Graphics);
|
||||
if (Output && Output->Resize(width, height))
|
||||
return true;
|
||||
if (Buffers && Buffers->Init((int32)((float)width * RenderingPercentage), (int32)((float)height * RenderingPercentage)))
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
float renderScale = RenderingPercentage * RenderScale;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
if (Buffers && Buffers->Init((int32)((float)width * renderScale), (int32)((float)height * renderScale)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -477,12 +486,6 @@ void MainRenderTask::OnBegin(GPUContext* context)
|
||||
// Use the main camera for the game (can be later overriden in Begin event by external code)
|
||||
Camera = Camera::GetMainCamera();
|
||||
|
||||
#if !USE_EDITOR
|
||||
// Sync render buffers size with the backbuffer
|
||||
const auto size = Screen::GetSize();
|
||||
Buffers->Init((int32)(size.X * RenderingPercentage), (int32)(size.Y * RenderingPercentage));
|
||||
#endif
|
||||
|
||||
SceneRenderTask::OnBegin(context);
|
||||
}
|
||||
|
||||
@@ -507,3 +510,10 @@ RenderContextBatch::RenderContextBatch(const RenderContext& context)
|
||||
Contexts.Add(context);
|
||||
EnableAsync = JobSystem::GetThreadsCount() > 1;
|
||||
}
|
||||
|
||||
void RenderContextBatch::FlushWaitLabels()
|
||||
{
|
||||
for (const int64 label : WaitLabels)
|
||||
JobSystem::Wait(label);
|
||||
WaitLabels.Clear();
|
||||
}
|
||||
|
||||
@@ -268,8 +268,14 @@ public:
|
||||
|
||||
/// <summary>
|
||||
/// The scale of the rendering resolution relative to the output dimensions. If lower than 1 the scene and postprocessing will be rendered at a lower resolution and upscaled to the output backbuffer.
|
||||
/// [Deprecated in v1.13]
|
||||
/// </summary>
|
||||
API_FIELD() float RenderingPercentage = 1.0f;
|
||||
API_FIELD() DEPRECATED("Use RenderScale instead.") float RenderingPercentage = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The scale of the rendering resolution relative to the output dimensions. If lower than 1 the scene and postprocessing will be rendered at a lower resolution and upscaled to the output backbuffer.
|
||||
/// </summary>
|
||||
API_FIELD() float RenderScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The image resolution upscale location within rendering pipeline. Unused if RenderingPercentage is 1.
|
||||
@@ -533,4 +539,9 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch
|
||||
{
|
||||
return Contexts.Get()[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for all scheduled async jobs to complete and clears WaitLabels.
|
||||
/// </summary>
|
||||
void FlushWaitLabels();
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ void CmdBufferVulkan::End()
|
||||
if (vkCmdEndDebugUtilsLabelEXT)
|
||||
vkCmdEndDebugUtilsLabelEXT(GetHandle());
|
||||
#endif
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
tracy::EndVkZoneScope(_tracyZones.Last().Data);
|
||||
_tracyZones.RemoveLast();
|
||||
#endif
|
||||
@@ -101,7 +101,7 @@ void CmdBufferVulkan::BeginEvent(const Char* name, void* tracyContext)
|
||||
char buffer[60];
|
||||
int32 bufferSize = StringUtils::Copy(buffer, name, sizeof(buffer));
|
||||
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
auto& zone = _tracyZones.AddOne();
|
||||
tracy::BeginVkZoneScope(zone.Data, tracyContext, GetHandle(), buffer, bufferSize);
|
||||
#endif
|
||||
@@ -128,7 +128,7 @@ void CmdBufferVulkan::EndEvent()
|
||||
vkCmdEndDebugUtilsLabelEXT(GetHandle());
|
||||
#endif
|
||||
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
tracy::EndVkZoneScope(_tracyZones.Last().Data);
|
||||
_tracyZones.RemoveLast();
|
||||
#endif
|
||||
|
||||
@@ -43,8 +43,10 @@ private:
|
||||
FenceVulkan* _fence;
|
||||
#if GPU_ALLOW_PROFILE_EVENTS
|
||||
int32 _eventsBegin = 0;
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
struct TracyZone { byte Data[TracyVulkanZoneSize]; };
|
||||
Array<TracyZone, InlinedAllocation<32>> _tracyZones;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The latest value when command buffer was submitted.
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
#define VULKAN_USE_TIMER_QUERIES 1
|
||||
#endif
|
||||
|
||||
#ifndef VULKAN_USE_TRACY_GPU
|
||||
#define VULKAN_USE_TRACY_GPU (GPU_ENABLE_TRACY && VULKAN_USE_TIMER_QUERIES)
|
||||
#endif
|
||||
|
||||
// Fence wait operation timeout in seconds
|
||||
#ifndef VULKAN_WAIT_TIMEOUT
|
||||
#define VULKAN_WAIT_TIMEOUT 5.0f
|
||||
|
||||
@@ -112,7 +112,7 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue)
|
||||
_handlesSizes[(int32)SpirvShaderResourceBindingType::UAV] = GPU_MAX_UA_BINDED;
|
||||
#endif
|
||||
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
#if VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset && !PLATFORM_SWITCH
|
||||
// Use calibrated timestamps extension
|
||||
if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT && _device->PhysicalDeviceFeatures12.hostQueryReset)
|
||||
@@ -139,7 +139,7 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue)
|
||||
|
||||
GPUContextVulkan::~GPUContextVulkan()
|
||||
{
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
tracy::DestroyVkContext(_tracyContext);
|
||||
#endif
|
||||
for (int32 i = 0; i < _descriptorPools.Count(); i++)
|
||||
@@ -921,7 +921,7 @@ void GPUContextVulkan::FrameEnd()
|
||||
// Execute any queued layout transitions that weren't already handled by the render pass
|
||||
FlushBarriers();
|
||||
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
if (cmdBuffer)
|
||||
tracy::CollectVkContext(_tracyContext, cmdBuffer->GetHandle());
|
||||
#endif
|
||||
@@ -935,7 +935,7 @@ void GPUContextVulkan::FrameEnd()
|
||||
void GPUContextVulkan::EventBegin(const Char* name)
|
||||
{
|
||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||
#if COMPILE_WITH_PROFILER
|
||||
#if VULKAN_USE_TRACY_GPU
|
||||
void* tracyContext = _tracyContext;
|
||||
#else
|
||||
void* tracyContext = nullptr;
|
||||
|
||||
@@ -44,7 +44,7 @@ static const char* GInstanceExtensions[] =
|
||||
#if defined(VK_KHR_display) && 0
|
||||
VK_KHR_DISPLAY_EXTENSION_NAME,
|
||||
#endif
|
||||
#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
#if VULKAN_USE_TRACY_GPU && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, // Required by VK_EXT_host_query_reset (unless using Vulkan 1.1 or newer)
|
||||
#endif
|
||||
nullptr
|
||||
@@ -65,7 +65,7 @@ static const char* GDeviceExtensions[] =
|
||||
#if VK_KHR_sampler_mirror_clamp_to_edge
|
||||
VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
|
||||
#endif
|
||||
#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
#if VULKAN_USE_TRACY_GPU && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME,
|
||||
VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
|
||||
#endif
|
||||
|
||||
@@ -1732,7 +1732,7 @@ bool GPUDeviceVulkan::Init()
|
||||
VulkanPlatform::RestrictEnabledPhysicalDeviceFeatures(PhysicalDeviceFeatures, enabledFeatures);
|
||||
deviceInfo.pEnabledFeatures = &enabledFeatures;
|
||||
|
||||
#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
#if VULKAN_USE_TRACY_GPU && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
VkPhysicalDeviceHostQueryResetFeatures resetFeatures;
|
||||
if (PhysicalDeviceFeatures12.hostQueryReset)
|
||||
{
|
||||
|
||||
@@ -820,8 +820,7 @@ void AnimatedModel::RunBlendShapeDeformer(const MeshBase* mesh, MeshDeformationD
|
||||
|
||||
void AnimatedModel::BeginPlay(SceneBeginData* data)
|
||||
{
|
||||
if (SkinnedModel && SkinnedModel->IsLoaded())
|
||||
PreInitSkinningData();
|
||||
PreInitSkinningData();
|
||||
|
||||
// Base
|
||||
ModelInstanceActor::BeginPlay(data);
|
||||
@@ -1263,9 +1262,7 @@ void AnimatedModel::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
SERIALIZE(ShadowsMode);
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
SERIALIZE(RootMotionTarget);
|
||||
|
||||
stream.JKEY("Buffer");
|
||||
stream.Object(&Entries, other ? &other->Entries : nullptr);
|
||||
SERIALIZE_MEMBER(Buffer, Entries);
|
||||
}
|
||||
|
||||
void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
@@ -1290,8 +1287,7 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
|
||||
DESERIALIZE(ShadowsMode);
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
DESERIALIZE(RootMotionTarget);
|
||||
|
||||
Entries.DeserializeIfExists(stream, "Buffer", modifier);
|
||||
DESERIALIZE_MEMBER(Buffer, Entries);
|
||||
|
||||
// [Deprecated on 07.02.2022, expires on 07.02.2024]
|
||||
if (modifier->EngineBuild <= 6330)
|
||||
|
||||
@@ -98,99 +98,99 @@ public:
|
||||
/// <summary>
|
||||
/// The skinned model asset used for rendering.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Skeleton\")")
|
||||
AssetReference<SkinnedModel> SkinnedModel;
|
||||
|
||||
/// <summary>
|
||||
/// The animation graph asset used for the skinned mesh skeleton bones evaluation (controls the animation).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(15), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(15), DefaultValue(null), EditorDisplay(\"Skeleton\")")
|
||||
AssetReference<AnimationGraph> AnimationGraph;
|
||||
|
||||
/// <summary>
|
||||
/// If true, use per-bone motion blur on this skeletal model. It requires additional rendering, can be disabled to save performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Drawing\")")
|
||||
bool PerBoneMotionBlur = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, animation speed will be affected by the global timescale parameter.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(30), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(30), DefaultValue(true), EditorDisplay(\"Updating\")")
|
||||
bool UseTimeScale = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the animation will be updated even when an actor cannot be seen by any camera. Otherwise, the animations themselves will also stop running when the actor is off-screen.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Updating\")")
|
||||
bool UpdateWhenOffscreen = false;
|
||||
|
||||
/// <summary>
|
||||
/// The animation update delta timescale. Can be used to speed up animation playback or create slow motion effect.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Updating\")")
|
||||
float UpdateSpeed = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The animation update mode. Can be used to optimize the performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(50), DefaultValue(AnimationUpdateMode.Auto), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(50), DefaultValue(AnimationUpdateMode.Auto), EditorDisplay(\"Updating\")")
|
||||
AnimationUpdateMode UpdateMode = AnimationUpdateMode::Auto;
|
||||
|
||||
/// <summary>
|
||||
/// The master scale parameter for the actor bounding box. Helps to reduce mesh flickering effect on screen edges.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Drawing\")")
|
||||
float BoundsScale = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The custom bounds(in actor local space). If set to empty bounds then source skinned model bind pose bounds will be used.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Drawing\")")
|
||||
BoundingBox CustomBounds = BoundingBox::Zero;
|
||||
|
||||
/// <summary>
|
||||
/// The model Level Of Detail bias value. Allows to increase or decrease rendered model quality.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(80), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Skinned Model\", \"LOD Bias\")")
|
||||
API_FIELD(Attributes="EditorOrder(80), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Drawing\", \"LOD Bias\")")
|
||||
int32 LODBias = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(90), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Skinned Model\", \"Forced LOD\")")
|
||||
API_FIELD(Attributes="EditorOrder(90), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Drawing\", \"Forced LOD\")")
|
||||
int32 ForcedLOD = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The draw passes to use for rendering this object.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(100), DefaultValue(DrawPass.Default), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(100), DefaultValue(DrawPass.Default), EditorDisplay(\"Drawing\")")
|
||||
DrawPass DrawModes = DrawPass::Default;
|
||||
|
||||
/// <summary>
|
||||
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be used to control transparency drawing.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorDisplay(\"Skinned Model\"), EditorOrder(110), DefaultValue(0)")
|
||||
API_FIELD(Attributes="EditorOrder(110), DefaultValue(0), EditorDisplay(\"Drawing\")")
|
||||
int8 SortOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The shadows casting mode.
|
||||
/// [Deprecated on 26.10.2022, expires on 26.10.2024]
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Drawing\")")
|
||||
DEPRECATED() ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
|
||||
|
||||
/// <summary>
|
||||
/// The animation root motion apply target. If not specified the animated model will apply it itself.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(120), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
|
||||
API_FIELD(Attributes="EditorOrder(120), DefaultValue(null), EditorDisplay(\"Skeleton\")")
|
||||
ScriptingObjectReference<Actor> RootMotionTarget;
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// If checked, the skeleton pose will be shawn during debug shapes drawing.
|
||||
/// If checked, the skeleton pose will be shown during debug shapes drawing.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(200), EditorDisplay(\"Skinned Model\")") bool ShowDebugDrawSkeleton = false;
|
||||
API_FIELD(Attributes="EditorOrder(200), EditorDisplay(\"Skeleton\"), VisibleIf(nameof(ShowDebugDrawOptions))") bool ShowDebugDrawSkeleton = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
@@ -440,6 +440,16 @@ public:
|
||||
API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim = nullptr);
|
||||
|
||||
private:
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Used to hide <see cref="ShowDebugDrawSkeleton"/> options if when the skinned model or animation graph is null.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize") bool GetShowDebugDrawOptions() const
|
||||
{
|
||||
return SkinnedModel != nullptr && AnimationGraph != nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ApplyRootMotion(const Transform& rootMotionDelta);
|
||||
void SyncParameters();
|
||||
void RunBlendShapeDeformer(const MeshBase* mesh, struct MeshDeformationData& deformation);
|
||||
|
||||
@@ -497,9 +497,7 @@ void SplineModel::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
SERIALIZE_MEMBER(PreTransform, _preTransform)
|
||||
SERIALIZE(Model);
|
||||
SERIALIZE(DrawModes);
|
||||
|
||||
stream.JKEY("Buffer");
|
||||
stream.Object(&Entries, other ? &other->Entries : nullptr);
|
||||
SERIALIZE_MEMBER(Buffer, Entries);
|
||||
}
|
||||
|
||||
void SplineModel::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
@@ -514,8 +512,7 @@ void SplineModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
|
||||
DESERIALIZE_MEMBER(PreTransform, _preTransform);
|
||||
DESERIALIZE(Model);
|
||||
DESERIALIZE(DrawModes);
|
||||
|
||||
Entries.DeserializeIfExists(stream, "Buffer", modifier);
|
||||
DESERIALIZE_MEMBER(Buffer, Entries);
|
||||
|
||||
// [Deprecated on 07.02.2022, expires on 07.02.2024]
|
||||
if (modifier->EngineBuild <= 6330)
|
||||
|
||||
@@ -486,8 +486,7 @@ void StaticModel::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
Lightmap.Serialize(stream);
|
||||
}
|
||||
|
||||
stream.JKEY("Buffer");
|
||||
stream.Object(&Entries, other ? &other->Entries : nullptr);
|
||||
SERIALIZE_MEMBER(Buffer, Entries);
|
||||
|
||||
if (_vertexColorsCount)
|
||||
{
|
||||
@@ -525,7 +524,7 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
|
||||
DESERIALIZE_MEMBER(SortOrder, _sortOrder);
|
||||
DESERIALIZE_MEMBER(DrawModes, _drawModes);
|
||||
Lightmap.Deserialize(stream);
|
||||
Entries.DeserializeIfExists(stream, "Buffer", modifier);
|
||||
DESERIALIZE_MEMBER(Buffer, Entries);
|
||||
|
||||
{
|
||||
const auto member = stream.FindMember("VertexColors");
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/MainThreadTask.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
|
||||
// Apply flow:
|
||||
// - collect all prefabs using this prefab (load and create default instances)
|
||||
@@ -772,7 +773,13 @@ bool Prefab::ApplyAll(Actor* targetActor)
|
||||
if (ApplyAllInternal(targetActor, true, thisPrefabInstancesData))
|
||||
return true;
|
||||
|
||||
SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData);
|
||||
// Sync nested prefabs
|
||||
if (allPrefabs.HasItems())
|
||||
{
|
||||
LOG(Info, "Updating referencing prefabs");
|
||||
HashSet<Guid> synced;
|
||||
SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData, synced);
|
||||
}
|
||||
|
||||
const auto endTime = DateTime::NowUTC();
|
||||
LOG(Info, "Prefab updated! {0} ms", (int32)(endTime - startTime).GetTotalMilliseconds());
|
||||
@@ -1027,8 +1034,14 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
rapidjson_flax::Document targetDataDocument;
|
||||
if (NestedPrefabs.HasItems())
|
||||
{
|
||||
// Use initial data buffer (unstripped) but reorder objects to match the sequence (eg. when new object was added to the nested prefab)
|
||||
targetDataDocument.Parse(dataBuffer.GetString(), dataBuffer.GetSize());
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, targetDataDocument, modifier.Value);
|
||||
Array<SceneObject*> reorderedObjects = *sceneObjects.Value;
|
||||
newPrefabInstanceIdToDataIndexCounter = 0;
|
||||
for (auto i = newPrefabInstanceIdToDataIndex.Begin(); i.IsNotEnd(); ++i)
|
||||
reorderedObjects.Insert(i->Value, sceneObjects->At(newPrefabInstanceIdToDataIndexStart + newPrefabInstanceIdToDataIndexCounter++));
|
||||
reorderedObjects.Resize(sceneObjects.Value->Count()); // reorderedObjects matches order in targetDataDocument
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(reorderedObjects, targetDataDocument, modifier.Value);
|
||||
SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData);
|
||||
|
||||
if (context.Instances.HasItems())
|
||||
@@ -1236,7 +1249,7 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
||||
{
|
||||
return Init(TypeName, StringAnsiView(tmpBuffer.GetString(), (int32)tmpBuffer.GetSize()));
|
||||
}
|
||||
#if 1 // Set to 0 to use memory-only reload that does not modifies the source file - useful for testing and debugging prefabs apply
|
||||
#if 1 // Set to 0 to use memory-only reload that does not modify the source file - useful for testing and debugging prefabs apply
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
Locker.Unlock();
|
||||
|
||||
@@ -1295,7 +1308,7 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
||||
_defaultInstance->DeleteObject();
|
||||
_defaultInstance = nullptr;
|
||||
}
|
||||
_isLoaded = false;
|
||||
_loadState = 0;
|
||||
|
||||
// Update prefab data manually (to prevent updating source asset file - just for testing)
|
||||
Document.Parse(buffer.GetString(), buffer.GetSize());
|
||||
@@ -1348,7 +1361,7 @@ bool Prefab::UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, r
|
||||
NestedPrefabs.Add(prefabId);
|
||||
}
|
||||
}
|
||||
_isLoaded = true;
|
||||
_loadState = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1395,34 +1408,31 @@ bool Prefab::SyncChangesInternal(PrefabInstancesData& prefabInstancesData)
|
||||
return ApplyAllInternal(targetActor, false, prefabInstancesData);
|
||||
}
|
||||
|
||||
void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData) const
|
||||
void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData, HashSet<Guid>& synced) const
|
||||
{
|
||||
PROFILE_CPU();
|
||||
LOG(Info, "Updating referencing prefabs");
|
||||
|
||||
// TODO: this may not work well for very complex prefab nesting -> loop order matters, maybe build a graph of dependencies?
|
||||
|
||||
// Call recursive for all referencing prefab assets to refresh nested prefabs
|
||||
for (int32 i = 0; i < allPrefabs.Count(); i++)
|
||||
{
|
||||
auto nestedPrefab = allPrefabs[i].Get();
|
||||
if (nestedPrefab)
|
||||
Prefab* nestedPrefab = allPrefabs[i].Get();
|
||||
if (!nestedPrefab || synced.Contains(nestedPrefab->GetID()))
|
||||
continue;
|
||||
if (nestedPrefab->WaitForLoaded())
|
||||
{
|
||||
if (nestedPrefab->WaitForLoaded())
|
||||
{
|
||||
LOG(Warning, "Waiting for prefab asset load failed.");
|
||||
continue;
|
||||
}
|
||||
LOG(Warning, "Waiting for '{}' load failed.", nestedPrefab->ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sync only if prefab is used by this prefab (directly) and it has been captured before
|
||||
const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID());
|
||||
if (nestedPrefabIndex != -1)
|
||||
{
|
||||
if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[i]))
|
||||
continue;
|
||||
nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData);
|
||||
ObjectsRemovalService::Flush();
|
||||
}
|
||||
// Sync only if prefab is used by this prefab (directly) and it has been captured before
|
||||
const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID());
|
||||
if (nestedPrefabIndex != -1)
|
||||
{
|
||||
synced.Add(nestedPrefab->GetID());
|
||||
if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[i]))
|
||||
continue;
|
||||
nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData, synced);
|
||||
ObjectsRemovalService::Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Level/Prefabs/PrefabManager.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
|
||||
@@ -22,6 +23,7 @@ Prefab::Prefab(const SpawnParams& params, const AssetInfo* info)
|
||||
|
||||
Guid Prefab::GetRootObjectId() const
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ASSERT(IsLoaded());
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
@@ -57,11 +59,12 @@ Actor* Prefab::GetDefaultInstance()
|
||||
// Skip if already created (reuse cached result)
|
||||
if (_defaultInstance)
|
||||
return _defaultInstance;
|
||||
PROFILE_CPU();
|
||||
|
||||
// Skip if not loaded
|
||||
if (!IsLoaded())
|
||||
{
|
||||
LOG(Warning, "Cannot instantiate object from not loaded prefab asset '{}'", GetPath());
|
||||
LOG(Warning, "Cannot instantiate object from not loaded prefab asset ({}, {})", GetPath(), GetID());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ private:
|
||||
bool ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData);
|
||||
bool UpdateInternal(const Array<SceneObject*>& defaultInstanceObjects, rapidjson_flax::StringBuffer& tmpBuffer);
|
||||
bool SyncChangesInternal(PrefabInstancesData& prefabInstancesData);
|
||||
void SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData) const;
|
||||
void SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array<PrefabInstancesData>& allPrefabsInstancesData, HashSet<Guid, HeapAllocation>& synced) const;
|
||||
#endif
|
||||
void DeleteDefaultInstance();
|
||||
|
||||
|
||||
@@ -752,7 +752,7 @@ void SceneObjectsFactory::SynchronizePrefabInstances(Context& context, PrefabSyn
|
||||
obj->SetOrderInParent(order);
|
||||
}
|
||||
|
||||
// Setup hierarchy for the prefab instances (ensure any new objects are connected)
|
||||
// Setup hierarchy for the prefab instances (after adding new objects to ensure they are connected, eg. when reparenting existing prefab into a new root)
|
||||
for (const auto& instance : context.Instances)
|
||||
{
|
||||
const auto& prefabStartData = data.Data[instance.StatIndex];
|
||||
|
||||
@@ -27,65 +27,41 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* nod
|
||||
switch (param->Type.Type)
|
||||
{
|
||||
case VariantType::Float2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsFloat2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Float3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsFloat3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Float4:
|
||||
case VariantType::Color:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsFloat4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double2:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsDouble2().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double3:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsDouble3().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Double4:
|
||||
switch (box->ID)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsDouble4().Raw[box->ID - 1];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VariantType::Int2:
|
||||
if (box->ID >= 1 && box->ID <= 2)
|
||||
value = value.AsInt2().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Int3:
|
||||
if (box->ID >= 1 && box->ID <= 3)
|
||||
value = value.AsInt3().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Int4:
|
||||
if (box->ID >= 1 && box->ID <= 4)
|
||||
value = value.AsInt4().Raw[box->ID - 1];
|
||||
break;
|
||||
case VariantType::Matrix:
|
||||
{
|
||||
|
||||
@@ -184,6 +184,15 @@ PhysicsScene* Physics::FindScene(const StringView& name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Physics::DeleteScene(PhysicsScene* scene)
|
||||
{
|
||||
if (scene == nullptr || scene == DefaultScene)
|
||||
return;
|
||||
scene->CollectResults();
|
||||
Scenes.RemoveKeepOrder(scene);
|
||||
Delete(scene);
|
||||
}
|
||||
|
||||
bool Physics::GetAutoSimulation()
|
||||
{
|
||||
return !DefaultScene || DefaultScene->GetAutoSimulation();
|
||||
|
||||
@@ -32,6 +32,11 @@ API_CLASS(Static) class FLAXENGINE_API Physics
|
||||
/// </summary>
|
||||
API_FUNCTION() static PhysicsScene* FindScene(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Delete an existing scene (excluding the default one).
|
||||
/// </summary>
|
||||
API_FUNCTION() static void DeleteScene(PhysicsScene* scene);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The automatic simulation feature. True if perform physics simulation after on fixed update by auto, otherwise user should do it.
|
||||
|
||||
@@ -75,6 +75,12 @@
|
||||
Platform::CheckFailed(#expression, __FILE__, __LINE__); \
|
||||
return returnValue; \
|
||||
}
|
||||
// Performs a soft check of the expression. Logs the expression failure and continues execution.
|
||||
#define CHECK_NO_RETURN(expression) \
|
||||
if (!(expression)) \
|
||||
{ \
|
||||
Platform::CheckFailed(#expression, __FILE__, __LINE__); \
|
||||
}
|
||||
|
||||
#if ENABLE_ASSERTION
|
||||
// Performs a soft check of the expression. Logs the expression failure and returns from the function call.
|
||||
|
||||
@@ -189,6 +189,7 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM
|
||||
POINT p;
|
||||
p.x = static_cast<LONG>(WINDOWS_GET_X_LPARAM(lParam));
|
||||
p.y = static_cast<LONG>(WINDOWS_GET_Y_LPARAM(lParam));
|
||||
const Float2 mousePosScreen(static_cast<float>(p.x), static_cast<float>(p.y));
|
||||
::ClientToScreen(window->GetHWND(), &p);
|
||||
const Float2 mousePos(static_cast<float>(p.x), static_cast<float>(p.y));
|
||||
|
||||
@@ -203,7 +204,8 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM
|
||||
}
|
||||
case WM_NCMOUSEMOVE:
|
||||
{
|
||||
OnMouseMove(mousePos, window);
|
||||
// The position in the message is already reported in screen-space
|
||||
OnMouseMove(mousePosScreen, window);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -356,9 +356,7 @@ void Renderer::DrawActors(RenderContext& renderContext, const Array<Actor*>& cus
|
||||
Level::DrawActors(renderContextBatch, SceneRendering::DrawCategory::SceneDraw);
|
||||
Level::DrawActors(renderContextBatch, SceneRendering::DrawCategory::SceneDrawAsync);
|
||||
JobSystem::SetJobStartingOnDispatch(true);
|
||||
for (const int64 label : renderContextBatch.WaitLabels)
|
||||
JobSystem::Wait(label);
|
||||
renderContextBatch.WaitLabels.Clear();
|
||||
renderContextBatch.FlushWaitLabels();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,9 +484,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
|
||||
// Wait for async jobs to finish
|
||||
JobSystem::SetJobStartingOnDispatch(true);
|
||||
for (const int64 label : renderContextBatch.WaitLabels)
|
||||
JobSystem::Wait(label);
|
||||
renderContextBatch.WaitLabels.Clear();
|
||||
renderContextBatch.FlushWaitLabels();
|
||||
|
||||
// Perform custom post-scene drawing (eg. GPU dispatches used by VFX)
|
||||
for (int32 i = 0; i < renderContextBatch.Contexts.Count(); i++)
|
||||
@@ -741,7 +737,9 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
}
|
||||
|
||||
// Upscaling after scene rendering but before post processing
|
||||
bool useUpscaling = task->RenderingPercentage < 1.0f;
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
bool useUpscaling = task->RenderingPercentage * task->RenderScale < 1.0f;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
const Viewport outputViewport = task->GetOutputViewport();
|
||||
if (useUpscaling && setup.UpscaleLocation == RenderingUpscaleLocation::BeforePostProcessingPass)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes enum serialization to use string names instead of integer values. This makes saved data resilient to enum reordering or changes in values (but not to renaming enums). Deserialization accepts both string names and integer values for backward compatibility.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Attribute" />
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public sealed class EnumStringAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul
|
||||
Struct.SetField = setField;
|
||||
}
|
||||
|
||||
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items)
|
||||
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items, bool stringSerialization)
|
||||
: ManagedClass(nullptr)
|
||||
, Module(module)
|
||||
, InitRuntime(DefaultInitRuntime)
|
||||
@@ -215,6 +215,7 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul
|
||||
, Size(size)
|
||||
{
|
||||
Enum.Items = items;
|
||||
Enum.StringSerialization = stringSerialization;
|
||||
}
|
||||
|
||||
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, InitRuntimeHandler initRuntime, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable, GetInterfaceWrapper getInterfaceWrapper)
|
||||
@@ -270,6 +271,7 @@ ScriptingType::ScriptingType(const ScriptingType& other)
|
||||
break;
|
||||
case ScriptingTypes::Enum:
|
||||
Enum.Items = other.Enum.Items;
|
||||
Enum.StringSerialization = other.Enum.StringSerialization;
|
||||
break;
|
||||
case ScriptingTypes::Interface:
|
||||
Interface.SetupScriptVTable = other.Interface.SetupScriptVTable;
|
||||
@@ -323,6 +325,7 @@ ScriptingType::ScriptingType(ScriptingType&& other)
|
||||
break;
|
||||
case ScriptingTypes::Enum:
|
||||
Enum.Items = other.Enum.Items;
|
||||
Enum.StringSerialization = other.Enum.StringSerialization;
|
||||
break;
|
||||
case ScriptingTypes::Interface:
|
||||
Interface.SetupScriptVTable = other.Interface.SetupScriptVTable;
|
||||
@@ -604,71 +607,57 @@ StringAnsiView ScriptingType::GetName() const
|
||||
return Fullname;
|
||||
}
|
||||
|
||||
#if BUILD_DEBUG || USE_EDITOR
|
||||
#define INIT_TYPE(...) \
|
||||
module->Types.AddUninitialized(); \
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, ##__VA_ARGS__); \
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname)) \
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName())); \
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
#else
|
||||
#define INIT_TYPE(...) \
|
||||
module->Types.AddUninitialized(); \
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, ##__VA_ARGS__); \
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
#endif
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SpawnHandler spawn, ScriptingTypeInitializer* baseType, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, const ScriptingType::InterfaceImplementation* interfaces)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Script
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, spawn, baseType, setupScriptVTable, setupScriptObjectVTable, interfaces);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(size, initRuntime, spawn, baseType, setupScriptVTable, setupScriptObjectVTable, interfaces);
|
||||
}
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Class
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, baseType, interfaces);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(size, initRuntime, ctor, dtor, baseType, interfaces);
|
||||
}
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Structure
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, copy, box, unbox, getField, setField, baseType, interfaces);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(size, initRuntime, ctor, dtor, copy, box, unbox, getField, setField, baseType, interfaces);
|
||||
}
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items)
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items, bool stringSerialization)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Enum
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, items);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(size, items, stringSerialization);
|
||||
}
|
||||
|
||||
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, ScriptingType::GetInterfaceWrapper getInterfaceWrapper)
|
||||
: ScriptingTypeHandle(module, module->Types.Count())
|
||||
{
|
||||
// Interface
|
||||
module->Types.AddUninitialized();
|
||||
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, initRuntime, setupScriptVTable, setupScriptObjectVTable, getInterfaceWrapper);
|
||||
#if BUILD_DEBUG
|
||||
if (module->TypeNameToTypeIndex.ContainsKey(fullname))
|
||||
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
|
||||
#endif
|
||||
module->TypeNameToTypeIndex[fullname] = TypeIndex;
|
||||
INIT_TYPE(initRuntime, setupScriptVTable, setupScriptObjectVTable, getInterfaceWrapper);
|
||||
}
|
||||
|
||||
#undef INIT_TYPE
|
||||
|
||||
CriticalSection BinaryModule::Locker;
|
||||
|
||||
BinaryModule::BinaryModulesList& BinaryModule::GetModules()
|
||||
|
||||
@@ -609,6 +609,12 @@ MObject* MUtils::BoxVariant(const Variant& value)
|
||||
return MCore::Object::Box((void*)&value.AsData, Double3::TypeInitializer.GetClass());
|
||||
case VariantType::Double4:
|
||||
return MCore::Object::Box((void*)&value.AsData, Double4::TypeInitializer.GetClass());
|
||||
case VariantType::Int2:
|
||||
return MCore::Object::Box((void*)&value.AsData, Int2::TypeInitializer.GetClass());
|
||||
case VariantType::Int3:
|
||||
return MCore::Object::Box((void*)&value.AsData, Int3::TypeInitializer.GetClass());
|
||||
case VariantType::Int4:
|
||||
return MCore::Object::Box((void*)&value.AsData, Int4::TypeInitializer.GetClass());
|
||||
case VariantType::Color:
|
||||
return MCore::Object::Box((void*)&value.AsData, stdTypes.ColorClass);
|
||||
case VariantType::Guid:
|
||||
@@ -880,6 +886,12 @@ MClass* MUtils::GetClass(const VariantType& value)
|
||||
return Double3::TypeInitializer.GetClass();
|
||||
case VariantType::Double4:
|
||||
return Double4::TypeInitializer.GetClass();
|
||||
case VariantType::Int2:
|
||||
return Int2::TypeInitializer.GetClass();
|
||||
case VariantType::Int3:
|
||||
return Int3::TypeInitializer.GetClass();
|
||||
case VariantType::Int4:
|
||||
return Int4::TypeInitializer.GetClass();
|
||||
case VariantType::Color:
|
||||
return Color::TypeInitializer.GetClass();
|
||||
case VariantType::Guid:
|
||||
@@ -970,6 +982,12 @@ MClass* MUtils::GetClass(const Variant& value)
|
||||
return Double3::TypeInitializer.GetClass();
|
||||
case VariantType::Double4:
|
||||
return Double4::TypeInitializer.GetClass();
|
||||
case VariantType::Int2:
|
||||
return Int2::TypeInitializer.GetClass();
|
||||
case VariantType::Int3:
|
||||
return Int3::TypeInitializer.GetClass();
|
||||
case VariantType::Int4:
|
||||
return Int4::TypeInitializer.GetClass();
|
||||
case VariantType::Color:
|
||||
return stdTypes.ColorClass;
|
||||
case VariantType::Guid:
|
||||
@@ -1138,6 +1156,9 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
|
||||
CASE_IN_BUILD_TYPE(Double2, AsData);
|
||||
CASE_IN_BUILD_TYPE(Double3, AsData);
|
||||
CASE_IN_BUILD_TYPE(Double4, AsBlob.Data);
|
||||
CASE_IN_BUILD_TYPE(Int2, AsData);
|
||||
CASE_IN_BUILD_TYPE(Int3, AsData);
|
||||
CASE_IN_BUILD_TYPE(Int4, AsData);
|
||||
#undef CASE_IN_BUILD_TYPE
|
||||
if (klass->IsValueType())
|
||||
{
|
||||
|
||||
@@ -266,6 +266,8 @@ struct FLAXENGINE_API ScriptingType
|
||||
{
|
||||
// Enum items table (the last item name is null)
|
||||
EnumItem* Items;
|
||||
// Enum uses string names serialization instead of integer values.
|
||||
bool StringSerialization;
|
||||
} Enum;
|
||||
|
||||
struct
|
||||
@@ -290,7 +292,7 @@ struct FLAXENGINE_API ScriptingType
|
||||
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime = DefaultInitRuntime, SpawnHandler spawn = DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, SetupScriptVTableHandler setupScriptVTable = nullptr, SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr, const InterfaceImplementation* interfaces = nullptr);
|
||||
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces = nullptr);
|
||||
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces = nullptr);
|
||||
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items);
|
||||
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, EnumItem* items, bool stringSerialization);
|
||||
ScriptingType(const StringAnsiView& fullname, BinaryModule* module, InitRuntimeHandler initRuntime, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable, GetInterfaceWrapper getInterfaceWrapper);
|
||||
ScriptingType(const ScriptingType& other);
|
||||
ScriptingType(ScriptingType&& other);
|
||||
@@ -339,7 +341,7 @@ struct FLAXENGINE_API ScriptingTypeInitializer : ScriptingTypeHandle
|
||||
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime = ScriptingType::DefaultInitRuntime, ScriptingType::SpawnHandler spawn = ScriptingType::DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, ScriptingType::SetupScriptVTableHandler setupScriptVTable = nullptr, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr);
|
||||
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr);
|
||||
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr);
|
||||
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items);
|
||||
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::EnumItem* items, bool stringSerialization);
|
||||
ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, ScriptingType::GetInterfaceWrapper getInterfaceWrapper);
|
||||
};
|
||||
|
||||
|
||||
+13
-5
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace FlaxEngine.Json.JsonCustomSerializers
|
||||
@@ -44,6 +45,13 @@ namespace FlaxEngine.Json.JsonCustomSerializers
|
||||
((JsonObjectContract)contract).ItemReferenceLoopHandling = ReferenceLoopHandling.Serialize;
|
||||
}
|
||||
|
||||
// Check if use enum serialization as string
|
||||
var type = Nullable.GetUnderlyingType(objectType) ?? objectType;
|
||||
if (type.IsEnum && type.GetCustomAttribute<EnumStringAttribute>() != null)
|
||||
{
|
||||
contract.Converter = new StringEnumConverter();
|
||||
}
|
||||
|
||||
return contract;
|
||||
}
|
||||
|
||||
@@ -53,19 +61,19 @@ namespace FlaxEngine.Json.JsonCustomSerializers
|
||||
var contract = base.CreateDictionaryContract(objectType);
|
||||
|
||||
// Override contract to save enums keys as integer
|
||||
if (contract.DictionaryKeyType?.IsEnum ?? false)
|
||||
var keyType = contract.DictionaryKeyType;
|
||||
if ((keyType?.IsEnum ?? false) && keyType.GetCustomAttribute<EnumStringAttribute>() == null)
|
||||
{
|
||||
var enumType = contract.DictionaryKeyType;
|
||||
contract.DictionaryKeyResolver = name =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var e = Enum.Parse(enumType, name);
|
||||
var e = Enum.Parse(keyType, name);
|
||||
name = Convert.ToInt32(e).ToString();
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Ignore errors
|
||||
Debug.Logger.LogHandler.LogWrite(LogType.Warning, $"Failed to parse enum '{name}' as {keyType.Name}: {ex.Message}");
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
@@ -49,6 +49,54 @@ void ISerializable::DeserializeIfExists(DeserializeStream& stream, const char* m
|
||||
var = defaultValue;\
|
||||
}
|
||||
|
||||
void Serialization::SerializeEnum(ISerializable::SerializeStream& stream, uint32 v, ScriptingTypeHandle typeHandle)
|
||||
{
|
||||
if (typeHandle)
|
||||
{
|
||||
// Check if serialize enum as string
|
||||
const ScriptingType& type = typeHandle.GetType();
|
||||
if (type.Type == ScriptingTypes::Enum && type.Enum.StringSerialization)
|
||||
{
|
||||
const auto items = type.Enum.Items;
|
||||
for (int32 i = 0; items[i].Name; i++)
|
||||
{
|
||||
if (items[i].Value == v)
|
||||
{
|
||||
stream.String(items[i].Name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.Uint(v);
|
||||
}
|
||||
|
||||
int32 Serialization::DeserializeEnum(ISerializable::DeserializeStream& stream, ScriptingTypeHandle typeHandle)
|
||||
{
|
||||
if (stream.IsString() && typeHandle)
|
||||
{
|
||||
// Deserialize enum from string
|
||||
const ScriptingType& type = typeHandle.GetType();
|
||||
if (type.Type == ScriptingTypes::Enum)
|
||||
{
|
||||
const auto str = stream.GetStringAnsiView();
|
||||
const auto items = type.Enum.Items;
|
||||
for (int32 i = 0; items[i].Name; i++)
|
||||
{
|
||||
if (str == items[i].Name)
|
||||
{
|
||||
return (int32)items[i].Value;
|
||||
}
|
||||
}
|
||||
int32 result;
|
||||
if (!StringUtils::Parse(stream.GetString(), &result))
|
||||
return result;
|
||||
LOG(Warning, "Failed to parse enum '{}' as {}", str.ToString(), type.Fullname.ToString());
|
||||
}
|
||||
}
|
||||
return DeserializeInt(stream);
|
||||
}
|
||||
|
||||
bool Serialization::ShouldSerialize(const VariantType& v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || v != *(VariantType*)otherObj;
|
||||
@@ -129,7 +177,6 @@ void Serialization::Serialize(ISerializable::SerializeStream& stream, const Vari
|
||||
stream.Int64(v.AsInt64);
|
||||
break;
|
||||
case VariantType::Uint64:
|
||||
case VariantType::Enum:
|
||||
stream.Uint64(v.AsUint64);
|
||||
break;
|
||||
case VariantType::Float:
|
||||
@@ -222,6 +269,9 @@ void Serialization::Serialize(ISerializable::SerializeStream& stream, const Vari
|
||||
else
|
||||
stream.String("", 0);
|
||||
break;
|
||||
case VariantType::Enum:
|
||||
SerializeEnum(stream, (int32)v.AsUint64, v.Type.GetScriptingType());
|
||||
break;
|
||||
case VariantType::ManagedObject:
|
||||
case VariantType::Structure:
|
||||
{
|
||||
@@ -276,7 +326,6 @@ void Serialization::Deserialize(ISerializable::DeserializeStream& stream, Varian
|
||||
v.AsInt64 = value.GetInt64();
|
||||
break;
|
||||
case VariantType::Uint64:
|
||||
case VariantType::Enum:
|
||||
v.AsUint64 = value.GetUint64();
|
||||
break;
|
||||
case VariantType::Float:
|
||||
@@ -371,6 +420,9 @@ void Serialization::Deserialize(ISerializable::DeserializeStream& stream, Varian
|
||||
CHECK(value.IsString());
|
||||
v.SetTypename(value.GetStringAnsiView());
|
||||
break;
|
||||
case VariantType::Enum:
|
||||
v.AsInt64 = DeserializeEnum(value, v.Type.GetScriptingType());
|
||||
break;
|
||||
case VariantType::ManagedObject:
|
||||
case VariantType::Structure:
|
||||
{
|
||||
|
||||
@@ -38,12 +38,16 @@ namespace Serialization
|
||||
int32 result = 0;
|
||||
if (stream.IsInt())
|
||||
result = stream.GetInt();
|
||||
else if (stream.IsInt64())
|
||||
result = (int32)stream.GetInt64();
|
||||
else if (stream.IsFloat())
|
||||
result = (int32)stream.GetFloat();
|
||||
else if (stream.IsString())
|
||||
StringUtils::Parse(stream.GetString(), &result);
|
||||
return result;
|
||||
}
|
||||
FLAXENGINE_API void SerializeEnum(ISerializable::SerializeStream& stream, uint32 v, ScriptingTypeHandle typeHandle);
|
||||
FLAXENGINE_API int32 DeserializeEnum(ISerializable::DeserializeStream& stream, ScriptingTypeHandle typeHandle);
|
||||
|
||||
// In-build types
|
||||
|
||||
@@ -226,12 +230,12 @@ namespace Serialization
|
||||
template<typename T>
|
||||
inline typename TEnableIf<TIsEnum<T>::Value>::Type Serialize(ISerializable::SerializeStream& stream, const T& v, const void* otherObj)
|
||||
{
|
||||
stream.Uint((uint32)v);
|
||||
SerializeEnum(stream, (uint32)v, StaticType<T>());
|
||||
}
|
||||
template<typename T>
|
||||
inline typename TEnableIf<TIsEnum<T>::Value>::Type Deserialize(ISerializable::DeserializeStream& stream, T& v, ISerializeModifier* modifier)
|
||||
{
|
||||
v = (T)DeserializeInt(stream);
|
||||
v = (T)DeserializeEnum(stream, StaticType<T>());
|
||||
}
|
||||
|
||||
// Common types
|
||||
|
||||
@@ -288,12 +288,15 @@ void ReadStream::Read(Variant& data)
|
||||
break;
|
||||
}
|
||||
case VariantType::Float2:
|
||||
case VariantType::Int2:
|
||||
ReadBytes(&data.AsData, sizeof(Float2));
|
||||
break;
|
||||
case VariantType::Float3:
|
||||
case VariantType::Int3:
|
||||
ReadBytes(&data.AsData, sizeof(Float3));
|
||||
break;
|
||||
case VariantType::Float4:
|
||||
case VariantType::Int4:
|
||||
ReadBytes(&data.AsData, sizeof(Float4));
|
||||
break;
|
||||
case VariantType::Double2:
|
||||
@@ -687,12 +690,15 @@ void WriteStream::Write(const Variant& data)
|
||||
Write(id);
|
||||
break;
|
||||
case VariantType::Float2:
|
||||
case VariantType::Int2:
|
||||
WriteBytes(data.AsData, sizeof(Float2));
|
||||
break;
|
||||
case VariantType::Float3:
|
||||
case VariantType::Int3:
|
||||
WriteBytes(data.AsData, sizeof(Float3));
|
||||
break;
|
||||
case VariantType::Float4:
|
||||
case VariantType::Int4:
|
||||
WriteBytes(data.AsData, sizeof(Float4));
|
||||
break;
|
||||
case VariantType::Double2:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Level/Actors/EmptyActor.h"
|
||||
#include "Engine/Level/Actors/DirectionalLight.h"
|
||||
#include "Engine/Level/Actors/ExponentialHeightFog.h"
|
||||
#include "Engine/Level/Actors/AnimatedModel.h"
|
||||
#include "Engine/Level/Prefabs/Prefab.h"
|
||||
#include "Engine/Level/Prefabs/PrefabManager.h"
|
||||
#include "Engine/Scripting/ScriptingObjectReference.h"
|
||||
@@ -905,4 +906,124 @@ TEST_CASE("Prefabs")
|
||||
instance1->DeleteObject();
|
||||
instance2->DeleteObject();
|
||||
}
|
||||
SECTION("Test Adding Object To Base Prefab")
|
||||
{
|
||||
// https://github.com/LOOPDISK/FlaxEngine/pull/44
|
||||
|
||||
// Create inner prefab with 3 objects in hierarchy
|
||||
AssetReference<Prefab> prefabInner = Content::CreateVirtualAsset<Prefab>();
|
||||
REQUIRE(prefabInner);
|
||||
Guid id;
|
||||
Guid::Parse("15dbe4b0416be0777a6ce59e8788b10f", id);
|
||||
prefabInner->ChangeID(id);
|
||||
auto prefabInnerInit = prefabInner->Init(Prefab::TypeName,
|
||||
"["
|
||||
"{"
|
||||
"\"ID\": \"3de462104f56f681c14650a0171f88fb\","
|
||||
"\"TypeName\" : \"FlaxEngine.SpotLight\","
|
||||
"\"Name\" : \"Inner.Root\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"19b181f846b6911635ffacb902c93c6a\","
|
||||
"\"TypeName\" : \"FlaxEngine.StaticModel\","
|
||||
"\"ParentID\" : \"3de462104f56f681c14650a0171f88fb\","
|
||||
"\"Name\" : \"Inner.Cube\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"8950889f4a2e752d55165fbf10eaf184\","
|
||||
"\"TypeName\" : \"FlaxEngine.AnimatedModel\","
|
||||
"\"ParentID\" : \"19b181f846b6911635ffacb902c93c6a\","
|
||||
"\"Name\" : \"Inner.Model\""
|
||||
"}"
|
||||
"]");
|
||||
REQUIRE(!prefabInnerInit);
|
||||
|
||||
// Create outer prefab with 2 instances of inner prefab
|
||||
AssetReference<Prefab> prefabOuter = Content::CreateVirtualAsset<Prefab>();
|
||||
REQUIRE(prefabOuter);
|
||||
SCOPE_EXIT{ Content::DeleteAsset(prefabOuter); };
|
||||
Guid::Parse("2ab744714f746e31855f41815612d14b", id);
|
||||
prefabOuter->ChangeID(id);
|
||||
auto prefabOuterInit = prefabOuter->Init(Prefab::TypeName,
|
||||
"["
|
||||
"{"
|
||||
"\"ID\": \"dba7f4bb4acfd62608b9a8bf550f31a5\","
|
||||
"\"TypeName\": \"FlaxEngine.EmptyActor\","
|
||||
"\"Name\": \"Outer.Root\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"a3b705284432bed9f043829c04a2bc8f\","
|
||||
"\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"3de462104f56f681c14650a0171f88fb\","
|
||||
"\"ParentID\": \"dba7f4bb4acfd62608b9a8bf550f31a5\","
|
||||
"\"Name\": \"Instance 1\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"06a8c15a41b822dd27f3ac9d79b142d3\","
|
||||
"\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"19b181f846b6911635ffacb902c93c6a\","
|
||||
"\"ParentID\": \"a3b705284432bed9f043829c04a2bc8f\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"4759fb9e4c4dda3b61ab5ab43949e42f\","
|
||||
"\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"8950889f4a2e752d55165fbf10eaf184\","
|
||||
"\"ParentID\": \"06a8c15a41b822dd27f3ac9d79b142d3\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"1225be664c0c081e714bbf93e09b99e4\","
|
||||
"\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"3de462104f56f681c14650a0171f88fb\","
|
||||
"\"ParentID\": \"dba7f4bb4acfd62608b9a8bf550f31a5\","
|
||||
"\"Name\": \"Instance 2\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"b397243540322182b806ad8339b7b617\","
|
||||
"\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"19b181f846b6911635ffacb902c93c6a\","
|
||||
"\"ParentID\": \"1225be664c0c081e714bbf93e09b99e4\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"2c3b8e824daf038a58df528a238ca2de\","
|
||||
"\"PrefabID\": \"15dbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"8950889f4a2e752d55165fbf10eaf184\","
|
||||
"\"ParentID\": \"b397243540322182b806ad8339b7b617\""
|
||||
"}"
|
||||
"]");
|
||||
REQUIRE(!prefabOuterInit);
|
||||
|
||||
// Spawn test instances of both prefabs
|
||||
ScriptingObjectReference<Actor> instanceInner = PrefabManager::SpawnPrefab(prefabInner);
|
||||
ScriptingObjectReference<Actor> instanceOuter = PrefabManager::SpawnPrefab(prefabOuter);
|
||||
|
||||
// Add new object to the inner prefab
|
||||
instanceInner->Children[0]->GetOrAddChild<DirectionalLight>();
|
||||
|
||||
// Apply changes
|
||||
bool applyResult = PrefabManager::ApplyAll(instanceInner);
|
||||
REQUIRE(!applyResult);
|
||||
|
||||
// Check state of outer instance to properly reflect hierarchy
|
||||
REQUIRE(instanceOuter);
|
||||
REQUIRE(instanceOuter->Children.Count() == 2);
|
||||
REQUIRE(instanceOuter->Children[0] != nullptr);
|
||||
REQUIRE(instanceOuter->Children[0]->Children.Count() == 1);
|
||||
REQUIRE(instanceOuter->Children[0]->Children[0]);
|
||||
REQUIRE(instanceOuter->Children[0]->Children[0]->Children.Count() == 2);
|
||||
REQUIRE(instanceOuter->Children[0]->Children[0]->Children[0]->Is<AnimatedModel>());
|
||||
REQUIRE(instanceOuter->Children[0]->Children[0]->Children[1]->Is<DirectionalLight>());
|
||||
REQUIRE(instanceOuter->Children[1] != nullptr);
|
||||
REQUIRE(instanceOuter->Children[1]->Children.Count() == 1);
|
||||
REQUIRE(instanceOuter->Children[1]->Children[0]);
|
||||
REQUIRE(instanceOuter->Children[1]->Children[0]->Children.Count() == 2);
|
||||
REQUIRE(instanceOuter->Children[1]->Children[0]->Children[0]->Is<AnimatedModel>());
|
||||
REQUIRE(instanceOuter->Children[0]->Children[0]->Children[1]->Is<DirectionalLight>());
|
||||
REQUIRE(instanceOuter->Children[0]->Children[0] != instanceOuter->Children[1]->Children[0]);
|
||||
REQUIRE(instanceOuter->Children[0]->Children[0]->Children[0] != instanceOuter->Children[1]->Children[0]->Children[0]);
|
||||
REQUIRE(instanceOuter->Children[0]->Children[0]->Children[1] != instanceOuter->Children[1]->Children[0]->Children[1]);
|
||||
|
||||
// Cleanup
|
||||
instanceInner->DeleteObject();
|
||||
instanceOuter->DeleteObject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
/// <summary>
|
||||
/// The size of a single sample in bits. The clip will be converted to this bit depth on import.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(50), VisibleIf(nameof(ShowBtiDepth))")
|
||||
API_FIELD(Attributes="EditorOrder(50), VisibleIf(nameof(ShowBitDepth))")
|
||||
BitDepth BitDepth = BitDepth::_16;
|
||||
|
||||
String ToString() const;
|
||||
|
||||
@@ -589,6 +589,10 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj
|
||||
SERIALIZE(TriangleReduction);
|
||||
SERIALIZE(SloppyOptimization);
|
||||
SERIALIZE(LODTargetError);
|
||||
SERIALIZE(LODTargetErrorAbsolute);
|
||||
SERIALIZE(LODLockBorder);
|
||||
SERIALIZE(LODPreserveUVs);
|
||||
SERIALIZE(LODPreserveUVsWeight);
|
||||
SERIALIZE(ImportMaterials);
|
||||
SERIALIZE(CreateEmptyMaterialSlots);
|
||||
SERIALIZE(ImportMaterialsAsInstances);
|
||||
@@ -646,6 +650,10 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi
|
||||
DESERIALIZE(TriangleReduction);
|
||||
DESERIALIZE(SloppyOptimization);
|
||||
DESERIALIZE(LODTargetError);
|
||||
DESERIALIZE(LODTargetErrorAbsolute);
|
||||
DESERIALIZE(LODLockBorder);
|
||||
DESERIALIZE(LODPreserveUVs);
|
||||
DESERIALIZE(LODPreserveUVsWeight);
|
||||
DESERIALIZE(ImportMaterials);
|
||||
DESERIALIZE(CreateEmptyMaterialSlots);
|
||||
DESERIALIZE(ImportMaterialsAsInstances);
|
||||
@@ -1945,8 +1953,9 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
|
||||
// Automatic LOD generation
|
||||
if (options.GenerateLODs && options.LODCount > 1 && data.LODs.HasItems() && options.TriangleReduction < 1.0f - ZeroTolerance)
|
||||
{
|
||||
InitMeshOpt();
|
||||
PROFILE_CPU_NAMED("GenerateLODs");
|
||||
auto lodStartTime = DateTime::NowUTC();
|
||||
InitMeshOpt();
|
||||
float triangleReduction = Math::Saturate(options.TriangleReduction);
|
||||
int32 lodCount = Math::Max(options.LODCount, data.LODs.Count());
|
||||
int32 baseLOD = Math::Clamp(options.BaseLOD, 0, lodCount - 1);
|
||||
@@ -1983,13 +1992,51 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
|
||||
continue;
|
||||
indices.Clear();
|
||||
indices.Resize(srcMeshIndexCount);
|
||||
int32 dstMeshIndexCount = {};
|
||||
int32 dstMeshIndexCount = 0;
|
||||
if (options.SloppyOptimization)
|
||||
{
|
||||
PROFILE_CPU_NAMED("meshopt_simplifySloppy");
|
||||
dstMeshIndexCount = (int32)meshopt_simplifySloppy(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError);
|
||||
}
|
||||
else
|
||||
dstMeshIndexCount = (int32)meshopt_simplify(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError);
|
||||
if (dstMeshIndexCount <= 0 || dstMeshIndexCount > indices.Count())
|
||||
continue;
|
||||
{
|
||||
// Build simplification flags
|
||||
unsigned int simplifyOptions = 0;
|
||||
if (options.LODLockBorder)
|
||||
simplifyOptions |= meshopt_SimplifyLockBorder;
|
||||
if (options.LODTargetErrorAbsolute)
|
||||
simplifyOptions |= meshopt_SimplifyErrorAbsolute;
|
||||
if (options.LODPreserveUVs && srcMesh->UVs.HasItems())
|
||||
{
|
||||
// Pack UV channels as attributes for meshopt_simplifyWithAttributes
|
||||
int32 uvChannelCount = srcMesh->UVs.Count();
|
||||
int32 attributeCount = uvChannelCount * 2; // 2 floats (U, V) per channel
|
||||
Array<float> attributes;
|
||||
attributes.Resize(srcMeshVertexCount * attributeCount);
|
||||
Array<float> attributeWeights;
|
||||
attributeWeights.Resize(attributeCount);
|
||||
for (int32 ch = 0; ch < uvChannelCount; ch++)
|
||||
{
|
||||
for (int32 v = 0; v < srcMeshVertexCount; v++)
|
||||
{
|
||||
Float2 uv = srcMesh->UVs[ch][v];
|
||||
attributes[v * attributeCount + ch * 2 + 0] = uv.X;
|
||||
attributes[v * attributeCount + ch * 2 + 1] = uv.Y;
|
||||
}
|
||||
attributeWeights[ch * 2 + 0] = options.LODPreserveUVsWeight;
|
||||
attributeWeights[ch * 2 + 1] = options.LODPreserveUVsWeight;
|
||||
}
|
||||
PROFILE_CPU_NAMED("meshopt_simplifyWithAttributes");
|
||||
dstMeshIndexCount = (int32)meshopt_simplifyWithAttributes(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), attributes.Get(), sizeof(float) * attributeCount, attributeWeights.Get(), attributeCount, nullptr, dstMeshIndexCountTarget, options.LODTargetError, simplifyOptions, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
PROFILE_CPU_NAMED("meshopt_simplify");
|
||||
dstMeshIndexCount = (int32)meshopt_simplify(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError, simplifyOptions, nullptr);
|
||||
}
|
||||
}
|
||||
if (dstMeshIndexCount <= 0 || dstMeshIndexCount >= indices.Count())
|
||||
continue; // Skip if failed to generate LOD or it doesn't have less vertices than source
|
||||
indices.Resize(dstMeshIndexCount);
|
||||
|
||||
// Generate simplified vertex buffer remapping table (use only vertices from LOD index buffer)
|
||||
|
||||
@@ -296,20 +296,32 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(1100), EditorDisplay(\"Level Of Detail\", \"Generate LODs\"), VisibleIf(nameof(ShowGeometry))")
|
||||
bool GenerateLODs = false;
|
||||
// The index of the LOD from the source model data to use as a reference for following LODs generation.
|
||||
API_FIELD(Attributes="EditorOrder(1110), EditorDisplay(\"Level Of Detail\", \"Base LOD\"), VisibleIf(nameof(ShowGeometry)), Limit(0, 5, 0.065f)")
|
||||
API_FIELD(Attributes="EditorOrder(1110), EditorDisplay(\"Level Of Detail\", \"Base LOD\"), VisibleIf(nameof(ShowGenerateLODs)), Limit(0, 5, 0.065f)")
|
||||
int32 BaseLOD = 0;
|
||||
// The amount of LODs to include in the model (all remaining ones starting from Base LOD will be generated).
|
||||
API_FIELD(Attributes="EditorOrder(1120), EditorDisplay(\"Level Of Detail\", \"LOD Count\"), VisibleIf(nameof(ShowGeometry)), Limit(1, 6, 0.065f)")
|
||||
API_FIELD(Attributes="EditorOrder(1120), EditorDisplay(\"Level Of Detail\", \"LOD Count\"), VisibleIf(nameof(ShowGenerateLODs)), Limit(1, 6, 0.065f)")
|
||||
int32 LODCount = 4;
|
||||
// The target amount of triangles for the generated LOD (based on the higher LOD). Normalized to range 0-1. For instance 0.4 cuts the triangle count to 40%.
|
||||
API_FIELD(Attributes="EditorOrder(1130), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry)), Limit(0, 1, 0.001f)")
|
||||
API_FIELD(Attributes="EditorOrder(1130), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGenerateLODs)), Limit(0, 1, 0.001f)")
|
||||
float TriangleReduction = 0.5f;
|
||||
// Whether to do a sloppy mesh optimization. This is faster but does not follow the topology of the original mesh.
|
||||
API_FIELD(Attributes="EditorOrder(1140), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry))")
|
||||
API_FIELD(Attributes="EditorOrder(1140), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGenerateLODs))")
|
||||
bool SloppyOptimization = false;
|
||||
// Target error is an approximate measure of the deviation from the original mesh using distance normalized to [0,1] range (e.g. 0.01 means that simplifier will try to maintain the error to be below 1% of the mesh extents). Only used if Sloppy is unchecked.
|
||||
API_FIELD(Attributes="EditorOrder(1150), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(SloppyOptimization), true), VisibleIf(nameof(ShowGeometry)), Limit(0.01f, 1, 0.001f)")
|
||||
API_FIELD(Attributes="EditorOrder(1150), EditorDisplay(\"Level Of Detail\", \"LOD Target Error\"), VisibleIf(nameof(SloppyOptimization), true), VisibleIf(nameof(ShowGenerateLODs)), Limit(0.01f, 1, 0.001f)")
|
||||
float LODTargetError = 0.05f;
|
||||
// If checked, vertices on topological borders (edges without a paired triangle) will not be moved during simplification. Useful for meshes that tile or share edges with other meshes.
|
||||
API_FIELD(Attributes="EditorOrder(1170), EditorDisplay(\"Level Of Detail\", \"Lock Border\"), VisibleIf(nameof(SloppyOptimization), true), VisibleIf(nameof(ShowGenerateLODs))")
|
||||
bool LODLockBorder = false;
|
||||
// If checked, the target error will be treated as absolute rather than relative to the mesh extents. In that mode, error is defined in absolute units which can be universal across similar mesh types no matter their size.
|
||||
API_FIELD(Attributes="EditorOrder(1160), EditorDisplay(\"Level Of Detail\", \"LOD Target Error Absolute\"), VisibleIf(nameof(SloppyOptimization), true), VisibleIf(nameof(ShowGenerateLODs))")
|
||||
bool LODTargetErrorAbsolute = false;
|
||||
// If checked, UV channels will be included in the simplification error metric to preserve UV layout. Essential for trimsheets and atlased textures.
|
||||
API_FIELD(Attributes="EditorOrder(1180), EditorDisplay(\"Level Of Detail\", \"Preserve UVs\"), VisibleIf(nameof(SloppyOptimization), true), VisibleIf(nameof(ShowGenerateLODs))")
|
||||
bool LODPreserveUVs = false;
|
||||
// The weight of UV attributes in the simplification error metric. Higher values preserve UVs more aggressively at the cost of geometric quality. Only used when Preserve UVs is enabled.
|
||||
API_FIELD(Attributes="EditorOrder(1190), EditorDisplay(\"Level Of Detail\", \"Preserve UVs Weight\"), VisibleIf(nameof(LODPreserveUVs)), VisibleIf(nameof(SloppyOptimization), true), VisibleIf(nameof(ShowGenerateLODs)), Limit(0.001f, 1, 0.001f)")
|
||||
float LODPreserveUVsWeight = 0.01f;
|
||||
|
||||
public: // Materials
|
||||
|
||||
|
||||
@@ -572,12 +572,12 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Starts the mouse tracking. Used by the scrollbars, splitters, etc.
|
||||
/// </summary>
|
||||
/// <param name="useMouseScreenOffset">If set to <c>true</c> will use mouse screen offset.</param>
|
||||
/// <param name="screenSpaceMouseWrap">If set to <c>true</c> will wrap when it hits a screen border.</param>
|
||||
[NoAnimate]
|
||||
public void StartMouseCapture(bool useMouseScreenOffset = false)
|
||||
public void StartMouseCapture(bool screenSpaceMouseWrap = false)
|
||||
{
|
||||
var parent = Root;
|
||||
parent?.StartTrackingMouse(this, useMouseScreenOffset);
|
||||
parent?.StartTrackingMouse(this, screenSpaceMouseWrap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -256,6 +256,33 @@ void GraphUtilities::ApplySomeMathHere(Variant& v, Variant& a, MathOp1 op)
|
||||
vv.W = (double)op((float)aa.W);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int2:
|
||||
{
|
||||
Int2& vv = *(Int2*)v.AsData;
|
||||
const Int2& aa = *(const Int2*)a.AsData;
|
||||
vv.X = (int32)op((float)aa.X);
|
||||
vv.Y = (int32)op((float)aa.Y);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int3:
|
||||
{
|
||||
Int3& vv = *(Int3*)v.AsData;
|
||||
const Int3& aa = *(const Int3*)a.AsData;
|
||||
vv.X = (int32)op((float)aa.X);
|
||||
vv.Y = (int32)op((float)aa.Y);
|
||||
vv.Z = (int32)op((float)aa.Z);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int4:
|
||||
{
|
||||
Int4& vv = *(Int4*)v.AsData;
|
||||
const Int4& aa = *(const Int4*)a.AsData;
|
||||
vv.X = (int32)op((float)aa.X);
|
||||
vv.Y = (int32)op((float)aa.Y);
|
||||
vv.Z = (int32)op((float)aa.Z);
|
||||
vv.W = (int32)op((float)aa.W);
|
||||
break;
|
||||
}
|
||||
case VariantType::Quaternion:
|
||||
{
|
||||
Quaternion& vv = *(Quaternion*)v.AsData;
|
||||
@@ -381,6 +408,36 @@ void GraphUtilities::ApplySomeMathHere(Variant& v, Variant& a, Variant& b, MathO
|
||||
vv.W = (double)op((float)aa.W, (float)bb.W);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int2:
|
||||
{
|
||||
Int2& vv = *(Int2*)v.AsData;
|
||||
const Int2& aa = *(const Int2*)a.AsData;
|
||||
const Int2& bb = *(const Int2*)b.AsData;
|
||||
vv.X = (int32)op((float)aa.X, (float)bb.X);
|
||||
vv.Y = (int32)op((float)aa.Y, (float)bb.Y);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int3:
|
||||
{
|
||||
Int3& vv = *(Int3*)v.AsData;
|
||||
const Int3& aa = *(const Int3*)a.AsData;
|
||||
const Int3& bb = *(const Int3*)b.AsData;
|
||||
vv.X = (int32)op((float)aa.X, (float)bb.X);
|
||||
vv.Y = (int32)op((float)aa.Y, (float)bb.Y);
|
||||
vv.Z = (int32)op((float)aa.Z, (float)bb.Z);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int4:
|
||||
{
|
||||
Int4& vv = *(Int4*)v.AsData;
|
||||
const Int4& aa = *(const Int4*)a.AsData;
|
||||
const Int4& bb = *(const Int4*)b.AsData;
|
||||
vv.X = (int32)op((float)aa.X, (float)bb.X);
|
||||
vv.Y = (int32)op((float)aa.Y, (float)bb.Y);
|
||||
vv.Z = (int32)op((float)aa.Z, (float)bb.Z);
|
||||
vv.W = (int32)op((float)aa.W, (float)bb.W);
|
||||
break;
|
||||
}
|
||||
case VariantType::Quaternion:
|
||||
{
|
||||
Quaternion& vv = *(Quaternion*)v.AsData;
|
||||
@@ -499,6 +556,39 @@ void GraphUtilities::ApplySomeMathHere(Variant& v, Variant& a, Variant& b, Varia
|
||||
vv.W = (double)op((float)aa.W, (float)bb.W, (float)cc.W);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int2:
|
||||
{
|
||||
Int2& vv = *(Int2*)v.AsData;
|
||||
const Int3& aa = *(const Int2*)a.AsData;
|
||||
const Int3& bb = *(const Int2*)b.AsData;
|
||||
const Int3& cc = *(const Int2*)c.AsData;
|
||||
vv.X = (int32)op((float)aa.X, (float)bb.X, (float)cc.X);
|
||||
vv.Y = (int32)op((float)aa.Y, (float)bb.Y, (float)cc.Y);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int3:
|
||||
{
|
||||
Int3& vv = *(Int3*)v.AsData;
|
||||
const Int3& aa = *(const Int3*)a.AsData;
|
||||
const Int3& bb = *(const Int3*)b.AsData;
|
||||
const Int3& cc = *(const Int3*)c.AsData;
|
||||
vv.X = (int32)op((float)aa.X, (float)bb.X, (float)cc.X);
|
||||
vv.Y = (int32)op((float)aa.Y, (float)bb.Y, (float)cc.Y);
|
||||
vv.Z = (int32)op((float)aa.Z, (float)bb.Z, (float)cc.Z);
|
||||
break;
|
||||
}
|
||||
case VariantType::Int4:
|
||||
{
|
||||
Int4& vv = *(Int4*)v.AsData;
|
||||
const Int4& aa = *(const Int4*)a.AsData;
|
||||
const Int4& bb = *(const Int4*)b.AsData;
|
||||
const Int4& cc = *(const Int4*)c.AsData;
|
||||
vv.X = (int32)op((float)aa.X, (float)bb.X, (float)cc.X);
|
||||
vv.Y = (int32)op((float)aa.Y, (float)bb.Y, (float)cc.Y);
|
||||
vv.Z = (int32)op((float)aa.Z, (float)bb.Z, (float)cc.Z);
|
||||
vv.W = (int32)op((float)aa.W, (float)bb.W, (float)cc.W);
|
||||
break;
|
||||
}
|
||||
case VariantType::Quaternion:
|
||||
{
|
||||
Quaternion& vv = *(Quaternion*)v.AsData;
|
||||
|
||||
@@ -167,6 +167,15 @@ public:
|
||||
// Base
|
||||
return Base::onNodeLoaded(n);
|
||||
}
|
||||
void Clear() override
|
||||
{
|
||||
FloatCurves.Clear();
|
||||
Float2Curves.Clear();
|
||||
Float3Curves.Clear();
|
||||
Float4Curves.Clear();
|
||||
|
||||
Base::Clear();
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -766,7 +766,7 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
|
||||
structureValue = Variant::Cast(structureValue, typeVariantType);
|
||||
}
|
||||
structureValue.InvertInline(); // Extract any Float3/Int32 into Structure type from inlined format
|
||||
const ScriptingTypeHandle structureValueTypeHandle = Scripting::FindScriptingType(structureValue.Type.GetTypeName());
|
||||
const ScriptingTypeHandle structureValueTypeHandle = structureValue.Type.GetScriptingType();
|
||||
if (structureValue.Type.Type != VariantType::Structure || typeHandle != structureValueTypeHandle)
|
||||
{
|
||||
OnError(node, box, String::Format(TEXT("Cannot unpack value of type {0} to structure of type {1}"), structureValue.Type, typeName));
|
||||
@@ -952,7 +952,7 @@ void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
#define SAMPLE_CURVE(id, curves, type, graphType) \
|
||||
case id: \
|
||||
{ \
|
||||
const auto& curve = GetCurrentGraph()->curves[node->Data.Curve.CurveIndex]; \
|
||||
const auto& curve = GetCurrentGraph()->curves[node->CurveIndex]; \
|
||||
const float time = (float)tryGetValue(node->GetBox(0), Value::Zero); \
|
||||
value.Type = VariantType(VariantType::graphType); \
|
||||
curve.Evaluate(*(type*)value.AsData, time, false); \
|
||||
|
||||
@@ -42,42 +42,6 @@ public:
|
||||
template<class BoxType = VisjectGraphBox>
|
||||
class VisjectGraphNode : public GraphNode<BoxType>
|
||||
{
|
||||
public:
|
||||
struct CurveData
|
||||
{
|
||||
/// <summary>
|
||||
/// The curve index.
|
||||
/// </summary>
|
||||
int32 CurveIndex;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Custom cached data per node type. Compact to use as small amount of memory as possible.
|
||||
/// </summary>
|
||||
struct AdditionalData
|
||||
{
|
||||
union
|
||||
{
|
||||
CurveData Curve;
|
||||
|
||||
struct
|
||||
{
|
||||
void* Method;
|
||||
BinaryModule* Module;
|
||||
int32 ParamsCount;
|
||||
uint32 OutParamsMask;
|
||||
bool IsStatic;
|
||||
} InvokeMethod;
|
||||
|
||||
struct
|
||||
{
|
||||
void* Field;
|
||||
BinaryModule* Module;
|
||||
bool IsStatic;
|
||||
} GetSetField;
|
||||
};
|
||||
};
|
||||
|
||||
public:
|
||||
VisjectGraphNode()
|
||||
: GraphNode<BoxType>()
|
||||
@@ -85,10 +49,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The custom data (depends on node type). Used to cache data for faster usage at runtime.
|
||||
/// </summary>
|
||||
AdditionalData Data;
|
||||
int32 CurveIndex = MAX_uint16;
|
||||
|
||||
/// <summary>
|
||||
/// The asset references. Linked resources such as Animation assets are referenced in graph data as ID. We need to keep valid refs to them at runtime to keep data in memory.
|
||||
@@ -148,7 +109,7 @@ public:
|
||||
#define SETUP_CURVE(id, curves, access) \
|
||||
case id: \
|
||||
{ \
|
||||
n->Data.Curve.CurveIndex = curves.Count(); \
|
||||
n->CurveIndex = curves.Count(); \
|
||||
auto& curve = curves.AddOne(); \
|
||||
const int32 keyframesCount = n->Values[0].AsInt; \
|
||||
auto& keyframes = curve.GetKeyframes(); \
|
||||
@@ -177,9 +138,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
return Base::onNodeLoaded(n);
|
||||
}
|
||||
void Clear() override
|
||||
{
|
||||
FloatCurves.Clear();
|
||||
Float2Curves.Clear();
|
||||
Float3Curves.Clear();
|
||||
Float4Curves.Clear();
|
||||
|
||||
Base::Clear();
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,13 +9,15 @@ bool VisjectMeta::Load(ReadStream* stream, bool loadData)
|
||||
{
|
||||
Release();
|
||||
|
||||
int32 entries;
|
||||
int32 entries = -1;
|
||||
stream->ReadInt32(&entries);
|
||||
if (entries < 0 || entries > MAX_uint16)
|
||||
return true;
|
||||
Entries.Resize(entries);
|
||||
|
||||
for (int32 i = 0; i < entries; i++)
|
||||
{
|
||||
Entry& e = Entries[i];
|
||||
Entry& e = Entries.Get()[i];
|
||||
|
||||
stream->ReadInt32(&e.TypeID);
|
||||
DateTime creationTime;
|
||||
|
||||
Reference in New Issue
Block a user