Merge remote-tracking branch 'origin/master' into 1.8
# Conflicts: # Source/Editor/Utilities/EditorUtilities.cpp # Source/Editor/Utilities/EditorUtilities.h
This commit is contained in:
@@ -413,10 +413,10 @@ bool Animation::Save(const StringView& path)
|
||||
MemoryWriteStream stream(4096);
|
||||
|
||||
// Info
|
||||
stream.WriteInt32(102);
|
||||
stream.WriteInt32(103);
|
||||
stream.WriteDouble(Data.Duration);
|
||||
stream.WriteDouble(Data.FramesPerSecond);
|
||||
stream.WriteBool(Data.EnableRootMotion);
|
||||
stream.WriteByte((byte)Data.RootMotionFlags);
|
||||
stream.WriteString(Data.RootNodeName, 13);
|
||||
|
||||
// Animation channels
|
||||
@@ -532,17 +532,22 @@ Asset::LoadResult Animation::load()
|
||||
int32 headerVersion = *(int32*)stream.GetPositionHandle();
|
||||
switch (headerVersion)
|
||||
{
|
||||
case 100:
|
||||
case 101:
|
||||
case 102:
|
||||
{
|
||||
case 103:
|
||||
stream.ReadInt32(&headerVersion);
|
||||
stream.ReadDouble(&Data.Duration);
|
||||
stream.ReadDouble(&Data.FramesPerSecond);
|
||||
Data.EnableRootMotion = stream.ReadBool();
|
||||
stream.ReadByte((byte*)&Data.RootMotionFlags);
|
||||
stream.ReadString(&Data.RootNodeName, 13);
|
||||
break;
|
||||
case 100:
|
||||
case 101:
|
||||
case 102:
|
||||
stream.ReadInt32(&headerVersion);
|
||||
stream.ReadDouble(&Data.Duration);
|
||||
stream.ReadDouble(&Data.FramesPerSecond);
|
||||
Data.RootMotionFlags = stream.ReadBool() ? AnimationRootMotionFlags::RootPositionXZ : AnimationRootMotionFlags::None;
|
||||
stream.ReadString(&Data.RootNodeName, 13);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
stream.ReadDouble(&Data.Duration);
|
||||
stream.ReadDouble(&Data.FramesPerSecond);
|
||||
|
||||
@@ -67,7 +67,7 @@ void AnimationGraph::OnDependencyModified(BinaryAsset* asset)
|
||||
|
||||
#endif
|
||||
|
||||
bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop)
|
||||
bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop, bool rootMotion)
|
||||
{
|
||||
if (!IsVirtual())
|
||||
{
|
||||
@@ -89,7 +89,7 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
|
||||
rootNode.Type = GRAPH_NODE_MAKE_TYPE(9, 1);
|
||||
rootNode.ID = 1;
|
||||
rootNode.Values.Resize(1);
|
||||
rootNode.Values[0] = (int32)RootMotionMode::NoExtraction;
|
||||
rootNode.Values[0] = (int32)(rootMotion ? RootMotionExtraction::Enable : RootMotionExtraction::Ignore);
|
||||
rootNode.Boxes.Resize(1);
|
||||
rootNode.Boxes[0] = AnimGraphBox(&rootNode, 0, VariantType::Void);
|
||||
auto& animNode = graph.Nodes[1];
|
||||
|
||||
@@ -37,8 +37,9 @@ public:
|
||||
/// <param name="baseModel">The base model asset.</param>
|
||||
/// <param name="anim">The animation to play.</param>
|
||||
/// <param name="loop">True if play animation in a loop.</param>
|
||||
/// <param name="rootMotion">True if apply root motion. Otherwise it will be ignored.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
API_FUNCTION() bool InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop = true);
|
||||
API_FUNCTION() bool InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop = true, bool rootMotion = false);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to load surface graph from the asset.
|
||||
|
||||
@@ -277,6 +277,8 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
|
||||
// Find asset in registry
|
||||
if (Cache.FindAsset(path, info))
|
||||
return true;
|
||||
if (!FileSystem::FileExists(path))
|
||||
return false;
|
||||
PROFILE_CPU();
|
||||
|
||||
const auto extension = FileSystem::GetExtension(path).ToLower();
|
||||
|
||||
@@ -393,34 +393,57 @@ bool JsonAsset::CreateInstance()
|
||||
if (typeHandle)
|
||||
{
|
||||
auto& type = typeHandle.GetType();
|
||||
|
||||
// Ensure that object can deserialized
|
||||
const ScriptingType::InterfaceImplementation* interface = type.GetInterface(ISerializable::TypeInitializer);
|
||||
if (!interface)
|
||||
{
|
||||
LOG(Warning, "Cannot deserialize {0} from Json Asset because it doesn't implement ISerializable interface.", type.ToString());
|
||||
return false;
|
||||
}
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
modifier->EngineBuild = DataEngineBuild;
|
||||
|
||||
// Create object
|
||||
switch (type.Type)
|
||||
{
|
||||
case ScriptingTypes::Class:
|
||||
case ScriptingTypes::Structure:
|
||||
{
|
||||
// Ensure that object can deserialized
|
||||
const ScriptingType::InterfaceImplementation* interface = type.GetInterface(ISerializable::TypeInitializer);
|
||||
if (!interface)
|
||||
{
|
||||
LOG(Warning, "Cannot deserialize {0} from Json Asset because it doesn't implement ISerializable interface.", type.ToString());
|
||||
break;
|
||||
}
|
||||
|
||||
// Allocate object
|
||||
const auto instance = Allocator::Allocate(type.Size);
|
||||
if (!instance)
|
||||
return true;
|
||||
Instance = instance;
|
||||
InstanceType = typeHandle;
|
||||
_dtor = type.Class.Dtor;
|
||||
type.Class.Ctor(instance);
|
||||
if (type.Type == ScriptingTypes::Class)
|
||||
{
|
||||
_dtor = type.Class.Dtor;
|
||||
type.Class.Ctor(instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dtor = type.Struct.Dtor;
|
||||
type.Struct.Ctor(instance);
|
||||
}
|
||||
|
||||
// Deserialize object
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
modifier->EngineBuild = DataEngineBuild;
|
||||
((ISerializable*)((byte*)instance + interface->VTableOffset))->Deserialize(*Data, modifier.Value);
|
||||
break;
|
||||
}
|
||||
case ScriptingTypes::Script:
|
||||
{
|
||||
const ScriptingObjectSpawnParams params(Guid::New(), typeHandle);
|
||||
const auto instance = type.Script.Spawn(params);
|
||||
if (!instance)
|
||||
return true;
|
||||
Instance = instance;
|
||||
_dtor = nullptr;
|
||||
|
||||
// Deserialize object
|
||||
ToInterface<ISerializable>(instance)->Deserialize(*Data, modifier.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
InstanceType = typeHandle;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -441,13 +464,20 @@ void JsonAsset::DeleteInstance()
|
||||
}
|
||||
|
||||
// C++ instance
|
||||
if (!Instance || !_dtor)
|
||||
if (!Instance)
|
||||
return;
|
||||
_dtor(Instance);
|
||||
if (_dtor)
|
||||
{
|
||||
_dtor(Instance);
|
||||
_dtor = nullptr;
|
||||
Allocator::Free(Instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
Delete((ScriptingObject*)Instance);
|
||||
}
|
||||
InstanceType = ScriptingTypeHandle();
|
||||
Allocator::Free(Instance);
|
||||
Instance = nullptr;
|
||||
_dtor = nullptr;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -139,7 +139,8 @@ public:
|
||||
T* GetInstance() const
|
||||
{
|
||||
const_cast<JsonAsset*>(this)->CreateInstance();
|
||||
return Instance && InstanceType.IsAssignableFrom(T::TypeInitializer) ? (T*)Instance : nullptr;
|
||||
const ScriptingTypeHandle& type = T::TypeInitializer;
|
||||
return Instance && type.IsAssignableFrom(InstanceType) ? (T*)Instance : nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
133
Source/Engine/Content/JsonAssetReference.cs
Normal file
133
Source/Engine/Content/JsonAssetReference.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Json asset reference utility. References resource with a typed data type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the asset instance type.</typeparam>
|
||||
#if FLAX_EDITOR
|
||||
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.AssetRefEditor))]
|
||||
#endif
|
||||
public struct JsonAssetReference<T> : IComparable, IComparable<JsonAssetReference<T>>, IEquatable<JsonAssetReference<T>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the referenced asset.
|
||||
/// </summary>
|
||||
public JsonAsset Asset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the serialized object from the json asset data. Cached internally.
|
||||
/// </summary>
|
||||
public T Instance => (T)Asset?.Instance;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonAssetReference{T}"/> structure.
|
||||
/// </summary>
|
||||
/// <param name="asset">The Json Asset.</param>
|
||||
public JsonAssetReference(JsonAsset asset)
|
||||
{
|
||||
Asset = asset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit cast operator.
|
||||
/// </summary>
|
||||
public static implicit operator JsonAsset(JsonAssetReference<T> value)
|
||||
{
|
||||
return value.Asset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit cast operator.
|
||||
/// </summary>
|
||||
public static implicit operator IntPtr(JsonAssetReference<T> value)
|
||||
{
|
||||
return Object.GetUnmanagedPtr(value.Asset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit cast operator.
|
||||
/// </summary>
|
||||
public static implicit operator JsonAssetReference<T>(JsonAsset value)
|
||||
{
|
||||
return new JsonAssetReference<T>(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit cast operator.
|
||||
/// </summary>
|
||||
public static implicit operator JsonAssetReference<T>(IntPtr valuePtr)
|
||||
{
|
||||
return new JsonAssetReference<T>(Object.FromUnmanagedPtr(valuePtr) as JsonAsset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the object exists (reference is not null and the unmanaged object pointer is valid).
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to check.</param>
|
||||
/// <returns>True if object is valid, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator bool(JsonAssetReference<T> obj)
|
||||
{
|
||||
return obj.Asset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two objects are equal.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(JsonAssetReference<T> left, JsonAssetReference<T> right)
|
||||
{
|
||||
return left.Asset == right.Asset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the two objects are not equal.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(JsonAssetReference<T> left, JsonAssetReference<T> right)
|
||||
{
|
||||
return left.Asset != right.Asset;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(JsonAssetReference<T> other)
|
||||
{
|
||||
return Asset == other.Asset;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(JsonAssetReference<T> other)
|
||||
{
|
||||
return Object.GetUnmanagedPtr(Asset).CompareTo(Object.GetUnmanagedPtr(other.Asset));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is JsonAssetReference<T> other && Asset == other.Asset;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Asset?.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return obj is JsonAssetReference<T> other ? CompareTo(other) : 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (Asset != null ? Asset.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Source/Engine/Content/JsonAssetReference.h
Normal file
41
Source/Engine/Content/JsonAssetReference.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
|
||||
/// <summary>
|
||||
/// Json asset reference utility. References resource with a typed data type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the asset instance type.</typeparam>
|
||||
template<typename T>
|
||||
API_STRUCT(NoDefault, Template, MarshalAs=JsonAsset*) struct JsonAssetReference : AssetReference<JsonAsset>
|
||||
{
|
||||
JsonAssetReference() = default;
|
||||
|
||||
JsonAssetReference(JsonAsset* asset)
|
||||
{
|
||||
OnSet(asset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type.
|
||||
/// </summary>
|
||||
/// <returns>The asset instance object or null.</returns>
|
||||
FORCE_INLINE T* GetInstance() const
|
||||
{
|
||||
return _asset ? Get()->template GetInstance<T>() : nullptr;
|
||||
}
|
||||
|
||||
JsonAssetReference& operator=(JsonAsset* asset) noexcept
|
||||
{
|
||||
OnSet(asset);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator JsonAsset*() const
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
};
|
||||
@@ -58,8 +58,8 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
|
||||
Locker.Lock();
|
||||
|
||||
// Try fast lookup
|
||||
FlaxStorage* result;
|
||||
if (!StorageMap.TryGet(path, result))
|
||||
FlaxStorage* storage;
|
||||
if (!StorageMap.TryGet(path, storage))
|
||||
{
|
||||
// Detect storage type and create object
|
||||
const bool isPackage = path.EndsWith(StringView(PACKAGE_FILES_EXTENSION));
|
||||
@@ -67,39 +67,42 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
|
||||
{
|
||||
auto package = New<FlaxPackage>(path);
|
||||
Packages.Add(package);
|
||||
result = package;
|
||||
storage = package;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto file = New<FlaxFile>(path);
|
||||
Files.Add(file);
|
||||
result = file;
|
||||
storage = file;
|
||||
}
|
||||
|
||||
// Register storage container
|
||||
StorageMap.Add(path, result);
|
||||
StorageMap.Add(path, storage);
|
||||
}
|
||||
|
||||
// Build reference (before releasing the lock so ContentStorageSystem::Job won't delete it when running from async thread)
|
||||
FlaxStorageReference result(storage);
|
||||
|
||||
Locker.Unlock();
|
||||
|
||||
if (loadIt)
|
||||
{
|
||||
// Initialize storage container
|
||||
result->LockChunks();
|
||||
const bool loadFailed = result->Load();
|
||||
result->UnlockChunks();
|
||||
storage->LockChunks();
|
||||
const bool loadFailed = storage->Load();
|
||||
storage->UnlockChunks();
|
||||
if (loadFailed)
|
||||
{
|
||||
LOG(Error, "Failed to load {0}.", path);
|
||||
Locker.Lock();
|
||||
StorageMap.Remove(path);
|
||||
if (result->IsPackage())
|
||||
Packages.Remove((FlaxPackage*)result);
|
||||
if (storage->IsPackage())
|
||||
Packages.Remove((FlaxPackage*)storage);
|
||||
else
|
||||
Files.Remove((FlaxFile*)result);
|
||||
Files.Remove((FlaxFile*)storage);
|
||||
Locker.Unlock();
|
||||
Delete(result);
|
||||
return nullptr;
|
||||
result = nullptr;
|
||||
Delete(storage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,13 @@ FlaxStorage::~FlaxStorage()
|
||||
|
||||
#if USE_EDITOR
|
||||
// Ensure to close any outstanding file handles to prevent file locking in case it failed to load
|
||||
_file.DeleteAll();
|
||||
Array<FileReadStream*> streams;
|
||||
_file.GetValues(streams);
|
||||
for (FileReadStream* stream : streams)
|
||||
{
|
||||
if (stream)
|
||||
Delete(stream);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1266,7 +1272,6 @@ bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data)
|
||||
}
|
||||
|
||||
#if ASSETS_LOADING_EXTRA_VERIFICATION
|
||||
|
||||
// Validate loaded header (asset ID and type ID must be the same)
|
||||
if (e.ID != data.Header.ID)
|
||||
{
|
||||
@@ -1276,7 +1281,6 @@ bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data)
|
||||
{
|
||||
LOG(Error, "Loading asset header data mismatch! Expected Type Name: {0}, loaded header: {1}.\nSource: {2}", e.TypeName, data.Header.ToString(), ToString());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return false;
|
||||
@@ -1339,7 +1343,14 @@ bool FlaxStorage::CloseFileHandles()
|
||||
return true; // Failed, someone is still accessing the file
|
||||
|
||||
// Close file handles (from all threads)
|
||||
_file.DeleteAll();
|
||||
Array<FileReadStream*> streams;
|
||||
_file.GetValues(streams);
|
||||
for (FileReadStream* stream : streams)
|
||||
{
|
||||
if (stream)
|
||||
Delete(stream);
|
||||
}
|
||||
_file.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ protected:
|
||||
CriticalSection _loadLocker;
|
||||
|
||||
// Storage
|
||||
ThreadLocalObject<FileReadStream> _file;
|
||||
ThreadLocal<FileReadStream*> _file;
|
||||
Array<FlaxChunk*> _chunks;
|
||||
|
||||
// Metadata
|
||||
|
||||
@@ -58,17 +58,17 @@ public:
|
||||
return _storage != nullptr;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator ==(const FlaxStorageReference& other) const
|
||||
FORCE_INLINE bool operator==(const FlaxStorageReference& other) const
|
||||
{
|
||||
return _storage == other._storage;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator !=(const FlaxStorageReference& other) const
|
||||
FORCE_INLINE bool operator!=(const FlaxStorageReference& other) const
|
||||
{
|
||||
return _storage != other._storage;
|
||||
}
|
||||
|
||||
FORCE_INLINE FlaxStorage* operator ->() const
|
||||
FORCE_INLINE FlaxStorage* operator->() const
|
||||
{
|
||||
return _storage;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user