Merge branch 'master' into lowlevel-networking

This commit is contained in:
Damian Korczowski
2021-05-22 19:26:34 +02:00
801 changed files with 47057 additions and 28368 deletions

View File

@@ -2,7 +2,8 @@
#pragma once
#include "Curve.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Animations/Curve.h"
#include "Engine/Core/Math/Transform.h"
/// <summary>

View File

@@ -6,7 +6,8 @@
#include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h"
Array<AnimatedModel*> UpdateList(256);
Array<AnimatedModel*> UpdateList;
Array<Matrix> UpdateBones;
class AnimationManagerService : public EngineService
{
@@ -67,10 +68,11 @@ void AnimationManagerService::Update()
}
animatedModel->GraphInstance.LastUpdateTime = t;
const auto bones = graph->GraphExecutor.Update(animatedModel->GraphInstance, dt);
const bool usePrevFrameBones = animatedModel->PerBoneMotionBlur;
animatedModel->_skinningData.SetData(bones, !usePrevFrameBones);
animatedModel->OnAnimUpdate();
// Evaluate animated nodes pose
graph->GraphExecutor.Update(animatedModel->GraphInstance, dt);
// Update gameplay
animatedModel->OnAnimationUpdated();
}
}
UpdateList.Clear();
@@ -79,6 +81,7 @@ void AnimationManagerService::Update()
void AnimationManagerService::Dispose()
{
UpdateList.Resize(0);
UpdateBones.Resize(0);
}
void AnimationManager::AddToUpdate(AnimatedModel* obj)

View File

@@ -70,7 +70,7 @@ namespace AnimationUtils
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& a, const Quaternion& b, float length, Quaternion& result)
{
const float oneThird = 1.0f / 3.0f;
Quaternion::Slerp(a, b, length * oneThird, result);
Quaternion::Slerp(a, b, oneThird, result);
}
template<>
@@ -79,7 +79,7 @@ namespace AnimationUtils
const float oneThird = 1.0f / 3.0f;
const float oneThirdLength = length * oneThird;
result.Translation = a.Translation + b.Translation * oneThirdLength;
Quaternion::Slerp(a.Orientation, b.Orientation, oneThirdLength, result.Orientation);
Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation);
result.Scale = a.Scale + (b.Scale - a.Scale) * oneThirdLength;
}

View File

@@ -10,6 +10,7 @@
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/MException.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
struct InternalInitData
@@ -144,6 +145,17 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
box->Cache = value;
}
bool AnimGraph::IsReady() const
{
return BaseModel && BaseModel->IsLoaded();
}
bool AnimGraph::CanUseWithSkeleton(SkinnedModel* other) const
{
// All data loaded and nodes count the same
return IsReady() && other && other->IsLoaded() && other->Skeleton.Nodes.Count() == BaseModel->Skeleton.Nodes.Count();
}
void AnimGraph::ClearCustomNode(Node* node)
{
// Clear data

View File

@@ -1,8 +1,10 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "AnimGraph.h"
#include "Engine/Engine/Time.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Graphics/Models/SkeletonData.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Engine/Time.h"
RootMotionData RootMotionData::Identity = { Vector3(0.0f), Quaternion(0.0f, 0.0f, 0.0f, 1.0f) };
@@ -173,7 +175,7 @@ AnimGraphExecutor::AnimGraphExecutor(AnimGraph& graph)
_perGroupProcessCall[16] = (ProcessBoxHandler)&AnimGraphExecutor::ProcessGroupFunction;
}
const Matrix* AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
{
ASSERT(data.Parameters.Count() == _graph.Parameters.Count());
@@ -183,7 +185,6 @@ const Matrix* AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
ANIM_GRAPH_PROFILE_EVENT("Init");
// Prepare graph data for the evaluation
_skeletonBonesCount = skeleton.Bones.Count();
_skeletonNodesCount = skeleton.Nodes.Count();
_graphStack.Clear();
_graphStack.Push((Graph*)&_graph);
@@ -263,23 +264,19 @@ const Matrix* AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
_data->RootMotion = animResult->RootMotion;
}
// Calculate the final bones transformations
{
ANIM_GRAPH_PROFILE_EVENT("Final Pose");
_bonesTransformations.Resize(_skeletonBonesCount, false);
for (int32 boneIndex = 0; boneIndex < _skeletonBonesCount; boneIndex++)
{
auto& bone = skeleton.Bones[boneIndex];
_bonesTransformations[boneIndex] = bone.OffsetMatrix * _data->NodesPose[bone.NodeIndex];
}
}
// Cleanup
_data = nullptr;
}
return _bonesTransformations.Get();
void AnimGraphExecutor::GetInputValue(Box* box, Value& result)
{
result = eatBox(box->GetParent<Node>(), box->FirstConnection());
}
void AnimGraphExecutor::ResetBucket(int32 bucketIndex)
{
auto& stateBucket = _data->State[bucketIndex];
_graph._bucketInitializerList[bucketIndex](stateBucket);
}
void AnimGraphExecutor::ResetBuckets(AnimGraphBase* graph)

View File

@@ -3,9 +3,9 @@
#pragma once
#include "Engine/Visject/VisjectGraph.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Content/Assets/Animation.h"
#include "Engine/Animations/AlphaBlend.h"
#include "Engine/Core/Math/Matrix.h"
#include "../Config.h"
#define ANIM_GRAPH_PARAM_BASE_MODEL_ID Guid(1000, 0, 0, 0)
@@ -21,6 +21,8 @@ class AnimSubGraph;
class AnimGraphBase;
class AnimGraphNode;
class AnimGraphExecutor;
class SkinnedModel;
class SkeletonData;
/// <summary>
/// The root motion data container. Supports displacement and rotation (no scale component).
@@ -777,22 +779,14 @@ public:
/// <summary>
/// Determines whether this graph is ready for the animation evaluation.
/// </summary>
/// <returns>True if is ready and can be used for the animation evaluation, otherwise false.</returns>
bool IsReady() const
{
return BaseModel && BaseModel->IsLoaded();
}
bool IsReady() const;
/// <summary>
/// Determines whether this graph can be used with the specified skeleton.
/// </summary>
/// <param name="other">The other skinned model to check.</param>
/// <returns>True if can perform the update, otherwise false.</returns>
bool CanUseWithSkeleton(SkinnedModel* other) const
{
// All data loaded and bones count the same
return IsReady() && other && other->IsLoaded() && other->Skeleton.Bones.Count() == BaseModel->Skeleton.Bones.Count();
}
bool CanUseWithSkeleton(SkinnedModel* other) const;
private:
@@ -823,12 +817,10 @@ private:
AnimGraph& _graph;
float _deltaTime = 0.0f;
uint64 _currentFrameIndex = 0;
int32 _skeletonBonesCount = 0;
int32 _skeletonNodesCount = 0;
RootMotionMode _rootMotionMode = RootMotionMode::NoExtraction;
AnimGraphInstanceData* _data = nullptr;
AnimGraphImpulse _emptyNodes;
Array<Matrix> _bonesTransformations;
AnimGraphTransitionData _transitionData;
Array<Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> _callStack;
Array<Graph*, FixedAllocation<32>> _graphStack;
@@ -859,18 +851,13 @@ public:
/// </summary>
/// <param name="data">The instance data.</param>
/// <param name="dt">The delta time (in seconds).</param>
/// <returns>The pointer to the final bones structure as a result of the animation evaluation.</returns>
const Matrix* Update(AnimGraphInstanceData& data, float dt);
void Update(AnimGraphInstanceData& data, float dt);
void GetInputValue(Box* box, Value& result)
{
result = eatBox(box->GetParent<Node>(), box->FirstConnection());
}
void GetInputValue(Box* box, Value& result);
/// <summary>
/// Gets the skeleton nodes transformations structure containing identity matrices.
/// </summary>
/// <returns>The data.</returns>
FORCE_INLINE const AnimGraphImpulse* GetEmptyNodes() const
{
return &_emptyNodes;
@@ -920,11 +907,7 @@ public:
/// Resets the state bucket.
/// </summary>
/// <param name="bucketIndex">The zero-based index of the bucket.</param>
FORCE_INLINE void ResetBucket(int32 bucketIndex)
{
auto& stateBucket = _data->State[bucketIndex];
_graph._bucketInitializerList[bucketIndex](stateBucket);
}
void ResetBucket(int32 bucketIndex);
/// <summary>
/// Resets all the state bucket used by the given graph including sub-graphs (total). Can eb used to reset the animation state of the nested graph (including children).

View File

@@ -451,7 +451,69 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu
// Get parameter
int32 paramIndex;
const auto param = _graph.GetParameter((Guid)node->Values[0], paramIndex);
value = param ? _data->Parameters[paramIndex].Value : Value::Null;
if (param)
{
value = _data->Parameters[paramIndex].Value;
switch (param->Type.Type)
{
case VariantType::Vector2:
switch (box->ID)
{
case 1:
case 2:
value = value.AsVector2().Raw[box->ID - 1];
break;
}
break;
case VariantType::Vector3:
switch (box->ID)
{
case 1:
case 2:
case 3:
value = value.AsVector3().Raw[box->ID - 1];
break;
}
break;
case VariantType::Vector4:
case VariantType::Color:
switch (box->ID)
{
case 1:
case 2:
case 3:
case 4:
value = value.AsVector4().Raw[box->ID - 1];
break;
}
break;
case VariantType::Matrix:
{
auto& matrix = value.Type.Type == VariantType::Matrix && value.AsBlob.Data ? *(Matrix*)value.AsBlob.Data : Matrix::Identity;
switch (box->ID)
{
case 0:
value = matrix.GetRow1();
break;
case 1:
value = matrix.GetRow2();
break;
case 2:
value = matrix.GetRow3();
break;
case 3:
value = matrix.GetRow4();
break;
}
break;
}
}
}
else
{
// TODO: add warning that no parameter selected
value = Value::Zero;
}
break;
}
default:
@@ -574,7 +636,8 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
transform.Scale = (Vector3)tryGetValue(node->GetBox(4), Vector3::One);
// Skip if no change will be performed
if (boneIndex < 0 || boneIndex >= _skeletonBonesCount || transformMode == BoneTransformMode::None || transform.IsIdentity())
auto& skeleton = _graph.BaseModel->Skeleton;
if (boneIndex < 0 || boneIndex >= skeleton.Bones.Count() || transformMode == BoneTransformMode::None || (transformMode == BoneTransformMode::Add && transform.IsIdentity()))
{
// Pass through the input
value = Value::Null;
@@ -705,8 +768,9 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
const auto copyScale = (bool)node->Values[4];
// Skip if no change will be performed
if (srcBoneIndex < 0 || srcBoneIndex >= _skeletonBonesCount ||
dstBoneIndex < 0 || dstBoneIndex >= _skeletonBonesCount ||
const auto& skeleton = _graph.BaseModel->Skeleton;
if (srcBoneIndex < 0 || srcBoneIndex >= skeleton.Bones.Count() ||
dstBoneIndex < 0 || dstBoneIndex >= skeleton.Bones.Count() ||
!(copyTranslation || copyRotation || copyScale))
{
// Pass through the input
@@ -714,7 +778,6 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
box->Cache = value;
return;
}
const auto& skeleton = _graph.BaseModel->Skeleton;
// Copy bone data
Transform srcTransform = nodes->Nodes[skeleton.Bones[srcBoneIndex].NodeIndex];
@@ -738,7 +801,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
const auto boneIndex = (int32)node->Values[0];
const auto& skeleton = _graph.BaseModel->Skeleton;
const auto input = tryGetValue(node->GetBox(0), Value::Null);
if (ANIM_GRAPH_IS_VALID_PTR(input) && boneIndex >= 0 && boneIndex < _skeletonBonesCount)
if (ANIM_GRAPH_IS_VALID_PTR(input) && boneIndex >= 0 && boneIndex < skeleton.Bones.Count())
value = Variant(((AnimGraphImpulse*)input.AsPointer)->Nodes[skeleton.Bones[boneIndex].NodeIndex]);
else
value = Variant(Transform::Identity);
@@ -1566,7 +1629,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
transform.Scale = (Vector3)tryGetValue(node->GetBox(4), Vector3::One);
// Skip if no change will be performed
if (nodeIndex < 0 || nodeIndex >= _skeletonNodesCount || transformMode == BoneTransformMode::None || transform.IsIdentity())
if (nodeIndex < 0 || nodeIndex >= _skeletonNodesCount || transformMode == BoneTransformMode::None || (transformMode == BoneTransformMode::Add && transform.IsIdentity()))
{
// Pass through the input
value = Value::Null;

View File

@@ -8,7 +8,7 @@
#include "Engine/Audio/AudioClip.h"
#include "Engine/Graphics/PostProcessSettings.h"
REGISTER_BINARY_ASSET(SceneAnimation, "FlaxEngine.SceneAnimation", nullptr, false);
REGISTER_BINARY_ASSET(SceneAnimation, "FlaxEngine.SceneAnimation", false);
SceneAnimation::SceneAnimation(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)

View File

@@ -9,6 +9,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Audio/AudioClip.h"
#include "Engine/Audio/AudioSource.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h"
@@ -1132,6 +1133,6 @@ void SceneAnimationPlayer::OnTransformChanged()
// Base
Actor::OnTransformChanged();
_box = BoundingBox(_transform.Translation, _transform.Translation);
_box = BoundingBox(_transform.Translation);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
}

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Level/Actor.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Level/Actors/PostFxVolume.h"
#include "SceneAnimation.h"

View File

@@ -48,8 +48,7 @@ public class Audio : EngineModule
break;
case TargetPlatform.Switch:
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "Audio"));
//options.CompileEnv.PreprocessorDefinitions.Add("AUDIO_API_SWITCH"); // TODO: impl audio on switch
useNone = true;
options.CompileEnv.PreprocessorDefinitions.Add("AUDIO_API_SWITCH");
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}

View File

@@ -4,6 +4,7 @@
#include "Audio.h"
#include "AudioSource.h"
#include "AudioBackend.h"
#include "Engine/Core/Log.h"
#include "Engine/Content/Upgraders/AudioClipUpgrader.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
@@ -12,7 +13,7 @@
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/AudioTool.h"
REGISTER_BINARY_ASSET(AudioClip, "FlaxEngine.AudioClip", ::New<AudioClipUpgrader>(), false);
REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClipUpgrader, false);
bool AudioClip::StreamingTask::Run()
{

View File

@@ -63,7 +63,7 @@ void AudioListener::OnTransformChanged()
// Base
Actor::OnTransformChanged();
_box = BoundingBox(_transform.Translation, _transform.Translation);
_box = BoundingBox(_transform.Translation);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
if (IsActiveInHierarchy())

View File

@@ -461,7 +461,7 @@ void AudioSource::OnTransformChanged()
// Base
Actor::OnTransformChanged();
_box = BoundingBox(_transform.Translation, _transform.Translation);
_box = BoundingBox(_transform.Translation);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
if (IsActiveInHierarchy() && SourceIDs.HasItems())

View File

@@ -18,7 +18,7 @@
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#define FLAX_POS_TO_OAL(vec) -vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f
#define FLAX_POS_TO_OAL(vec) -vec.X * 0.01f, vec.Y * 0.01f, -vec.Z * 0.01f
namespace ALC
{

View File

@@ -26,7 +26,7 @@
#define MAX_OUTPUT_CHANNELS 8
#define MAX_CHANNELS_MATRIX_SIZE (MAX_INPUT_CHANNELS*MAX_OUTPUT_CHANNELS)
#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(-vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f)
#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f)
#define FLAX_VEC_TO_XAUDIO(vec) (*((X3DAUDIO_VECTOR*)&vec))
namespace XAudio2

View File

@@ -7,6 +7,7 @@
#include "Engine/Level/SceneQuery.h"
#include "Engine/Level/Actor.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Graphics/Models/ModelData.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h"

View File

@@ -2,6 +2,8 @@
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
namespace CSG
{
/// <summary>

View File

@@ -38,9 +38,10 @@ void Asset::OnDeleteObject()
if (!IsInternalType())
Content::AssetDisposing(this);
// Cache data
const bool wasMarkedToDelete = _deleteFileOnUnload != 0;
#if USE_EDITOR
const String path = wasMarkedToDelete ? GetPath() : String::Empty;
#endif
const Guid id = GetID();
// Fire unload event (every object referencing this asset or it's data should release reference so later actions are safe)
@@ -66,7 +67,7 @@ void Asset::OnDeleteObject()
// Base (after it `this` is invalid)
ManagedScriptingObject::OnDeleteObject();
// Check if asset was marked to delete
#if USE_EDITOR
if (wasMarkedToDelete)
{
LOG(Info, "Deleting asset '{0}':{1}.", path, id.ToString());
@@ -77,6 +78,7 @@ void Asset::OnDeleteObject()
// Delete file
Content::deleteFileSafety(path, id);
}
#endif
}
void Asset::CreateManaged()
@@ -131,6 +133,20 @@ void Asset::ChangeID(const Guid& newId)
CRASH;
}
bool Asset::LastLoadFailed() const
{
return _loadFailed != 0;
}
#if USE_EDITOR
bool Asset::ShouldDeleteFileOnUnload() const
{
return _deleteFileOnUnload != 0;
}
#endif
void Asset::Reload()
{
// It's better to call it from the main thread
@@ -225,7 +241,7 @@ bool Asset::WaitForLoaded(double timeoutInMilliseconds)
while (!Engine::ShouldExit())
{
// Try to execute content tasks
while (task->IsQueued())
while (task->IsQueued() && !Engine::ShouldExit())
{
// Pick this task from the queue
ContentLoadTask* tmp;
@@ -335,13 +351,21 @@ void Asset::startLoading()
bool Asset::onLoad(LoadAssetTask* task)
{
// It may fail when task is cancelled and new one is created later (don't crash but just end with an error)
if (task->GetAsset() != this || _loadingTask == nullptr)
if (task->Asset.Get() != this || _loadingTask == nullptr)
return true;
Locker.Lock();
// Load asset
const LoadResult result = loadAsset();
LoadResult result;
{
#if TRACY_ENABLE
ZoneScoped;
const StringView name(GetPath());
ZoneName(*name, name.Length());
#endif
result = loadAsset();
}
const bool isLoaded = result == LoadResult::Ok;
const bool failed = !isLoaded;
_loadFailed = failed;

View File

@@ -82,7 +82,6 @@ public:
/// <summary>
/// Gets asset's reference count. Asset will be automatically unloaded when this reaches zero.
/// </summary>
/// <returns>The amount of references to that asset.</returns>
API_PROPERTY() int32 GetReferencesCount() const
{
return (int32)Platform::AtomicRead(const_cast<int64 volatile*>(&_refCount));
@@ -107,21 +106,18 @@ public:
public:
/// <summary>
/// Gets the path to the asset storage.
/// Gets the path to the asset storage file. In Editor it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers.
/// </summary>
/// <returns>The asset file.</returns>
API_PROPERTY() virtual const String& GetPath() const = 0;
/// <summary>
/// Gets the asset type name.
/// </summary>
/// <returns>The typename.</returns>
virtual const String& GetTypeName() const = 0;
/// <summary>
/// Returns true if asset is loaded, otherwise false.
/// </summary>
/// <returns><c>true</c> if this asset is loaded; otherwise, <c>false</c>.</returns>
API_PROPERTY() FORCE_INLINE bool IsLoaded() const
{
return _isLoaded != 0;
@@ -130,16 +126,11 @@ public:
/// <summary>
/// Returns true if last asset loading failed, otherwise false.
/// </summary>
/// <returns><c>true</c> if last asset loading failed; otherwise, <c>false</c>.</returns>
API_PROPERTY() FORCE_INLINE bool LastLoadFailed() const
{
return _loadFailed != 0;
}
API_PROPERTY() bool LastLoadFailed() const;
/// <summary>
/// Determines whether this asset is virtual (generated or temporary, has no storage so it won't be saved).
/// </summary>
/// <returns><c>true</c> if this asset is virtual; otherwise, <c>false</c>.</returns>
API_PROPERTY() FORCE_INLINE bool IsVirtual() const
{
return _isVirtual != 0;
@@ -150,11 +141,7 @@ public:
/// <summary>
/// Determines whether this asset was marked to be deleted on unload.
/// </summary>
/// <returns><c>true</c> if this asset file was marked to be deleted on asset unload; otherwise, <c>false</c>.</returns>
API_PROPERTY() FORCE_INLINE bool ShouldDeleteFileOnUnload() const
{
return _deleteFileOnUnload != 0;
}
API_PROPERTY() bool ShouldDeleteFileOnUnload() const;
#endif

View File

@@ -274,3 +274,9 @@ public:
OnSet(asset);
}
};
template<typename T>
uint32 GetHash(const AssetReference<T>& key)
{
return GetHash(key.GetID());
}

View File

@@ -11,7 +11,7 @@
#include "Engine/Serialization/MemoryWriteStream.h"
#endif
REGISTER_BINARY_ASSET(Animation, "FlaxEngine.Animation", nullptr, false);
REGISTER_BINARY_ASSET(Animation, "FlaxEngine.Animation", false);
Animation::Animation(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)

View File

@@ -9,7 +9,7 @@
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
REGISTER_BINARY_ASSET(AnimationGraph, "FlaxEngine.AnimationGraph", nullptr, false);
REGISTER_BINARY_ASSET(AnimationGraph, "FlaxEngine.AnimationGraph", false);
AnimationGraph::AnimationGraph(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)
@@ -74,7 +74,7 @@ BytesContainer AnimationGraph::LoadSurface()
return result;
}
LOG(Warning, "Animation Graph \'{0}\' surface data is missing.", GetPath());
LOG(Warning, "Animation Graph \'{0}\' surface data is missing.", ToString());
return BytesContainer();
}

View File

@@ -6,7 +6,7 @@
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
REGISTER_BINARY_ASSET(AnimationGraphFunction, "FlaxEngine.AnimationGraphFunction", nullptr, false);
REGISTER_BINARY_ASSET(AnimationGraphFunction, "FlaxEngine.AnimationGraphFunction", false);
AnimationGraphFunction::AnimationGraphFunction(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)

View File

@@ -4,7 +4,7 @@
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/TextureAssetUpgrader.h"
REGISTER_BINARY_ASSET(CubeTexture, "FlaxEngine.CubeTexture", ::New<TextureAssetUpgrader>(), true);
REGISTER_BINARY_ASSET_WITH_UPGRADER(CubeTexture, "FlaxEngine.CubeTexture", TextureAssetUpgrader, true);
CubeTexture::CubeTexture(const SpawnParams& params, const AssetInfo* info)
: TextureBase(params, info)

View File

@@ -4,7 +4,7 @@
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/TextureAssetUpgrader.h"
REGISTER_BINARY_ASSET(IESProfile, "FlaxEngine.IESProfile", ::New<TextureAssetUpgrader>(), false);
REGISTER_BINARY_ASSET_WITH_UPGRADER(IESProfile, "FlaxEngine.IESProfile", TextureAssetUpgrader, false);
IESProfile::IESProfile(const SpawnParams& params, const AssetInfo* info)
: TextureBase(params, info)

View File

@@ -16,6 +16,9 @@
#include "Engine/Utilities/Encryption.h"
#include "Engine/Tools/MaterialGenerator/MaterialGenerator.h"
#include "Engine/ShadersCompilation/Config.h"
#if BUILD_DEBUG
#include "Engine/Engine/Globals.h"
#endif
#endif
/// <summary>
@@ -23,7 +26,7 @@
/// </summary>
#define MATERIAL_AUTO_GENERATE_MISSING_SOURCE (USE_EDITOR)
REGISTER_BINARY_ASSET(Material, "FlaxEngine.Material", ::New<ShaderAssetUpgrader>(), false);
REGISTER_BINARY_ASSET_WITH_UPGRADER(Material, "FlaxEngine.Material", ShaderAssetUpgrader, false);
Material::Material(const SpawnParams& params, const AssetInfo* info)
: ShaderAssetTypeBase<MaterialBase>(params, info)
@@ -116,7 +119,7 @@ Asset::LoadResult Material::load()
FlaxChunk* materialParamsChunk;
// Special case for Null renderer
if (GPUDevice::Instance->GetRendererType() == RendererType::Null)
if (IsNullRenderer())
{
// Hack loading
MemoryReadStream shaderCacheStream(nullptr, 0);
@@ -152,18 +155,16 @@ Asset::LoadResult Material::load()
// - If material version is not supported then material cannot be loaded
#if COMPILE_WITH_SHADER_COMPILER
#if BUILD_DEBUG
// Materials force reload!
Globals::ConvertLoadedMaterialsByForce = false;
#endif
// Check if current engine has different materials version or convert it by force or has no source generated at all
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION
|| Globals::ConvertLoadedMaterialsByForce
#if MATERIAL_AUTO_GENERATE_MISSING_SOURCE
|| !HasChunk(SHADER_FILE_CHUNK_SOURCE)
#endif
|| HasDependenciesModified()
#if COMPILE_WITH_DEV_ENV
// Set to true to enable force GPU shader regeneration (don't commit it)
|| false
#endif
)
{
// Prepare
@@ -310,7 +311,12 @@ Asset::LoadResult Material::load()
{
// Load material (load shader from cache, load params, setup pipeline stuff)
MemoryReadStream shaderCacheStream(shaderCache.Data.Get(), shaderCache.Data.Length());
_materialShader = MaterialShader::Create(GetPath(), shaderCacheStream, _shaderHeader.Material.Info);
#if GPU_ENABLE_RESOURCE_NAMING
const StringView name(GetPath());
#else
const StringView name;
#endif
_materialShader = MaterialShader::Create(name, shaderCacheStream, _shaderHeader.Material.Info);
if (_materialShader == nullptr)
{
LOG(Warning, "Cannot load material.");
@@ -482,7 +488,7 @@ BytesContainer Material::LoadSurface(bool createDefaultIfMissing)
}
}
LOG(Warning, "Material \'{0}\' surface data is missing.", GetPath());
LOG(Warning, "Material \'{0}\' surface data is missing.", ToString());
#if COMPILE_WITH_MATERIAL_GRAPH

View File

@@ -8,7 +8,7 @@
#endif
#include "Engine/Content/Factories/BinaryAssetFactory.h"
REGISTER_BINARY_ASSET(MaterialFunction, "FlaxEngine.MaterialFunction", nullptr, false);
REGISTER_BINARY_ASSET(MaterialFunction, "FlaxEngine.MaterialFunction", false);
MaterialFunction::MaterialFunction(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)

View File

@@ -7,7 +7,7 @@
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Serialization/MemoryReadStream.h"
REGISTER_BINARY_ASSET(MaterialInstance, "FlaxEngine.MaterialInstance", ::New<MaterialInstanceUpgrader>(), true);
REGISTER_BINARY_ASSET_WITH_UPGRADER(MaterialInstance, "FlaxEngine.MaterialInstance", MaterialInstanceUpgrader, true);
MaterialInstance::MaterialInstance(const SpawnParams& params, const AssetInfo* info)
: MaterialBase(params, info)

View File

@@ -8,9 +8,11 @@
#include "Engine/Content/Upgraders/ModelAssetUpgrader.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
#include "Engine/Streaming/StreamingGroup.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Renderer/DrawCall.h"
#if GPU_ENABLE_ASYNC_RESOURCES_CREATION
#include "Engine/Threading/ThreadPoolTask.h"
#define STREAM_TASK_BASE ThreadPoolTask
@@ -113,7 +115,7 @@ protected:
}
};
REGISTER_BINARY_ASSET(Model, "FlaxEngine.Model", ::New<ModelAssetUpgrader>(), true);
REGISTER_BINARY_ASSET_WITH_UPGRADER(Model, "FlaxEngine.Model", ModelAssetUpgrader, true);
Model::Model(const SpawnParams& params, const AssetInfo* info)
: ModelBase(params, info, StreamingGroups::Instance()->Models())
@@ -396,21 +398,21 @@ bool Model::Save(bool withMeshDataFromGpu, const StringView& path)
auto& meshData = meshesData[meshIndex];
// Vertex Buffer 0 (required)
auto task = mesh.ExtractDataAsync(MeshBufferType::Vertex0, meshData.VB0);
auto task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex0, meshData.VB0);
if (task == nullptr)
return true;
task->Start();
tasks.Add(task);
// Vertex Buffer 1 (required)
task = mesh.ExtractDataAsync(MeshBufferType::Vertex1, meshData.VB1);
task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex1, meshData.VB1);
if (task == nullptr)
return true;
task->Start();
tasks.Add(task);
// Vertex Buffer 2 (optional)
task = mesh.ExtractDataAsync(MeshBufferType::Vertex2, meshData.VB2);
task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex2, meshData.VB2);
if (task)
{
task->Start();
@@ -418,7 +420,7 @@ bool Model::Save(bool withMeshDataFromGpu, const StringView& path)
}
// Index Buffer (required)
task = mesh.ExtractDataAsync(MeshBufferType::Index, meshData.IB);
task = mesh.DownloadDataGPUAsync(MeshBufferType::Index, meshData.IB);
if (task == nullptr)
return true;
task->Start();
@@ -618,6 +620,19 @@ void Model::SetupMaterialSlots(int32 slotsCount)
}
}
int32 Model::GetLODsCount() const
{
return LODs.Count();
}
void Model::GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex)
{
auto& lod = LODs[lodIndex];
meshes.Resize(lod.Meshes.Count());
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
meshes[meshIndex] = &lod.Meshes[meshIndex];
}
void Model::InitAsVirtual()
{
// Init with a single LOD and one mesh
@@ -628,6 +643,21 @@ void Model::InitAsVirtual()
BinaryAsset::InitAsVirtual();
}
#if USE_EDITOR
void Model::GetReferences(Array<Guid>& output) const
{
// Base
BinaryAsset::GetReferences(output);
for (int32 i = 0; i < MaterialSlots.Count(); i++)
{
output.Add(MaterialSlots[i].Material.GetID());
}
}
#endif
int32 Model::GetMaxResidency() const
{
return LODs.Count();
@@ -791,7 +821,7 @@ Asset::LoadResult Model::load()
const auto thisSS = LODs[lodIndex].ScreenSize;
if (prevSS <= thisSS)
{
LOG(Warning, "Model LOD {0} has invalid screen size compared to LOD {1} (asset: {2})", lodIndex, lodIndex - 1, GetPath());
LOG(Warning, "Model LOD {0} has invalid screen size compared to LOD {1} (asset: {2})", lodIndex, lodIndex - 1, ToString());
}
}
#endif
@@ -837,3 +867,33 @@ AssetChunksFlag Model::getChunksToPreload() const
// Note: we don't preload any LODs here because it's done by the Streaming Manager
return GET_CHUNK_FLAG(0);
}
void ModelBase::SetupMaterialSlots(int32 slotsCount)
{
CHECK(slotsCount >= 0 && slotsCount < 4096);
if (!IsVirtual() && WaitForLoaded())
return;
ScopeLock lock(Locker);
const int32 prevCount = MaterialSlots.Count();
MaterialSlots.Resize(slotsCount, false);
// Initialize slot names
for (int32 i = prevCount; i < slotsCount; i++)
MaterialSlots[i].Name = String::Format(TEXT("Material {0}"), i + 1);
}
MaterialSlot* ModelBase::GetSlot(const StringView& name)
{
MaterialSlot* result = nullptr;
for (auto& slot : MaterialSlots)
{
if (slot.Name == name)
{
result = &slot;
break;
}
}
return result;
}

View File

@@ -55,15 +55,6 @@ public:
return LODs.HasItems();
}
/// <summary>
/// Gets amount of the level of details in the model
/// </summary>
/// <returns>Amount of the level of details in the model</returns>
FORCE_INLINE int32 GetLODsCount() const
{
return LODs.Count();
}
/// <summary>
/// Gets the amount of loaded model LODs.
/// </summary>
@@ -237,18 +228,11 @@ public:
// [ModelBase]
void SetupMaterialSlots(int32 slotsCount) override;
int32 GetLODsCount() const override;
void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) override;
void InitAsVirtual() override;
#if USE_EDITOR
void GetReferences(Array<Guid>& output) const override
{
// Base
BinaryAsset::GetReferences(output);
for (int32 i = 0; i < MaterialSlots.Count(); i++)
{
output.Add(MaterialSlots[i].Material.GetID());
}
}
void GetReferences(Array<Guid>& output) const override;
#endif
// [StreamableResource]

View File

@@ -7,6 +7,8 @@
#include "Engine/Graphics/Models/MaterialSlot.h"
#include "Engine/Streaming/StreamableResource.h"
class MeshBase;
/// <summary>
/// Base class for asset types that can contain a model resource.
/// </summary>
@@ -44,38 +46,23 @@ public:
/// <summary>
/// Resizes the material slots collection. Updates meshes that were using removed slots.
/// </summary>
API_FUNCTION() virtual void SetupMaterialSlots(int32 slotsCount)
{
CHECK(slotsCount >= 0 && slotsCount < 4096);
if (!IsVirtual() && WaitForLoaded())
return;
ScopeLock lock(Locker);
const int32 prevCount = MaterialSlots.Count();
MaterialSlots.Resize(slotsCount, false);
// Initialize slot names
for (int32 i = prevCount; i < slotsCount; i++)
MaterialSlots[i].Name = String::Format(TEXT("Material {0}"), i + 1);
}
API_FUNCTION() virtual void SetupMaterialSlots(int32 slotsCount);
/// <summary>
/// Gets the material slot by the name.
/// </summary>
/// <param name="name">The slot name.</param>
/// <returns>The material slot with the given name or null if cannot find it (asset may be not loaded yet).</returns>
API_FUNCTION() MaterialSlot* GetSlot(const StringView& name)
{
MaterialSlot* result = nullptr;
for (auto& slot : MaterialSlots)
{
if (slot.Name == name)
{
result = &slot;
break;
}
}
return result;
}
API_FUNCTION() MaterialSlot* GetSlot(const StringView& name);
/// <summary>
/// Gets amount of the level of details in the model.
/// </summary>
/// <returns>Amount of the level of details in the model.</returns>
virtual int32 GetLODsCount() const = 0;
/// <summary>
/// Gets the meshes for a particular LOD index.
/// </summary>
virtual void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) = 0;
};

View File

@@ -4,7 +4,7 @@
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Platform/FileSystem.h"
REGISTER_BINARY_ASSET(RawDataAsset, "FlaxEngine.RawDataAsset", nullptr, true);
REGISTER_BINARY_ASSET(RawDataAsset, "FlaxEngine.RawDataAsset", true);
RawDataAsset::RawDataAsset(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)

View File

@@ -2,11 +2,13 @@
#include "Shader.h"
#include "Engine/Core/Log.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Content/Upgraders/ShaderAssetUpgrader.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Serialization/MemoryReadStream.h"
REGISTER_BINARY_ASSET(Shader, "FlaxEngine.Shader", ::New<ShaderAssetUpgrader>(), false);
REGISTER_BINARY_ASSET_WITH_UPGRADER(Shader, "FlaxEngine.Shader", ShaderAssetUpgrader, false);
Shader::Shader(const SpawnParams& params, const AssetInfo* info)
: ShaderAssetTypeBase<BinaryAsset>(params, info)
@@ -25,7 +27,7 @@ Shader::~Shader()
Asset::LoadResult Shader::load()
{
// Special case for Null renderer that doesn't need shaders
if (GPUDevice::Instance->GetRendererType() == RendererType::Null)
if (IsNullRenderer())
{
return LoadResult::Ok;
}

View File

@@ -3,9 +3,10 @@
#pragma once
#include "../BinaryAsset.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/Shaders/Cache/ShaderAssetBase.h"
class GPUShader;
/// <summary>
/// The shader asset. Contains a program that runs on the GPU and is able to perform rendering calculation using textures, vertices and other resources.
/// </summary>
@@ -33,7 +34,6 @@ public:
/// <summary>
/// Gets the GPU shader object.
/// </summary>
/// <returns>The GPU shader object.</returns>
FORCE_INLINE GPUShader* GetShader() const
{
return GPU;

View File

@@ -7,7 +7,7 @@
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/SkeletonMaskUpgrader.h"
REGISTER_BINARY_ASSET(SkeletonMask, "FlaxEngine.SkeletonMask", ::New<SkeletonMaskUpgrader>(), true);
REGISTER_BINARY_ASSET_WITH_UPGRADER(SkeletonMask, "FlaxEngine.SkeletonMask", SkeletonMaskUpgrader, true);
SkeletonMask::SkeletonMask(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)

View File

@@ -7,12 +7,14 @@
#include "Engine/Streaming/StreamingGroup.h"
#include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
#include "Engine/Graphics/Models/Config.h"
#include "Engine/Content/WeakAssetReference.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Renderer/DrawCall.h"
#define CHECK_INVALID_BUFFER(buffer) \
if (buffer->IsValidFor(this) == false) \
@@ -106,7 +108,7 @@ protected:
}
};
REGISTER_BINARY_ASSET(SkinnedModel, "FlaxEngine.SkinnedModel", ::New<SkinnedModelAssetUpgrader>(), true);
REGISTER_BINARY_ASSET_WITH_UPGRADER(SkinnedModel, "FlaxEngine.SkinnedModel", SkinnedModelAssetUpgrader, true);
SkinnedModel::SkinnedModel(const SpawnParams& params, const AssetInfo* info)
: ModelBase(params, info, StreamingGroups::Instance()->SkinnedModels())
@@ -120,6 +122,11 @@ SkinnedModel::~SkinnedModel()
ASSERT(_streamingTask == nullptr);
}
bool SkinnedModel::HasAnyLODInitialized() const
{
return LODs.HasItems() && LODs.Last().HasAnyMeshInitialized();
}
Array<String> SkinnedModel::GetBlendShapes()
{
Array<String> result;
@@ -137,6 +144,18 @@ Array<String> SkinnedModel::GetBlendShapes()
return result;
}
ContentLoadTask* SkinnedModel::RequestLODDataAsync(int32 lodIndex)
{
const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
return RequestChunkDataAsync(chunkIndex);
}
void SkinnedModel::GetLODData(int32 lodIndex, BytesContainer& data) const
{
const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
GetChunkData(chunkIndex, data);
}
bool SkinnedModel::Intersects(const Ray& ray, const Matrix& world, float& distance, Vector3& normal, SkinnedMesh** mesh, int32 lodIndex)
{
return LODs[lodIndex].Intersects(ray, world, distance, normal, mesh);
@@ -389,7 +408,7 @@ bool SkinnedModel::Save(bool withMeshDataFromGpu, const StringView& path)
{
auto& slot = MaterialSlots[materialSlotIndex];
const auto id =slot.Material.GetID();
const auto id = slot.Material.GetID();
stream->Write(&id);
stream->WriteByte(static_cast<byte>(slot.ShadowsMode));
stream->WriteString(slot.Name, 11);
@@ -505,14 +524,14 @@ bool SkinnedModel::Save(bool withMeshDataFromGpu, const StringView& path)
auto& meshData = meshesData[meshIndex];
// Vertex Buffer 0 (required)
auto task = mesh.DownloadDataAsyncGPU(MeshBufferType::Vertex0, meshData.VB0);
auto task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex0, meshData.VB0);
if (task == nullptr)
return true;
task->Start();
tasks.Add(task);
// Index Buffer (required)
task = mesh.DownloadDataAsyncGPU(MeshBufferType::Index, meshData.IB);
task = mesh.DownloadDataGPUAsync(MeshBufferType::Index, meshData.IB);
if (task == nullptr)
return true;
task->Start();
@@ -701,6 +720,19 @@ void SkinnedModel::SetupMaterialSlots(int32 slotsCount)
}
}
int32 SkinnedModel::GetLODsCount() const
{
return LODs.Count();
}
void SkinnedModel::GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex)
{
auto& lod = LODs[lodIndex];
meshes.Resize(lod.Meshes.Count());
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
meshes[meshIndex] = &lod.Meshes[meshIndex];
}
void SkinnedModel::InitAsVirtual()
{
// Init with one mesh and single bone
@@ -723,6 +755,21 @@ void SkinnedModel::InitAsVirtual()
BinaryAsset::InitAsVirtual();
}
#if USE_EDITOR
void SkinnedModel::GetReferences(Array<Guid>& output) const
{
// Base
BinaryAsset::GetReferences(output);
for (int32 i = 0; i < MaterialSlots.Count(); i++)
{
output.Add(MaterialSlots[i].Material.GetID());
}
}
#endif
int32 SkinnedModel::GetMaxResidency() const
{
return LODs.Count();

View File

@@ -61,19 +61,9 @@ public:
return LODs.HasItems();
}
/// <summary>
/// Gets amount of the level of details in the model
/// </summary>
/// <returns>Amount of the level of details in the model</returns>
FORCE_INLINE int32 GetLODsCount() const
{
return LODs.Count();
}
/// <summary>
/// Gets the amount of loaded model LODs.
/// </summary>
/// <returns>Loaded LODs count</returns>
API_PROPERTY() FORCE_INLINE int32 GetLoadedLODs() const
{
return _loadedLODs;
@@ -102,7 +92,6 @@ public:
/// <summary>
/// Gets index of the highest resident LOD (may be equal to LODs.Count if no LOD has been uploaded). Note: LOD=0 is the highest (top quality)
/// </summary>
/// <returns>LOD index</returns>
FORCE_INLINE int32 HighestResidentLODIndex() const
{
return GetLODsCount() - _loadedLODs;
@@ -112,10 +101,7 @@ public:
/// Determines whether any LOD has been initialized.
/// </summary>
/// <returns>True if any LOD has been initialized, otherwise false.</returns>
FORCE_INLINE bool HasAnyLODInitialized() const
{
return LODs.HasItems() && LODs.Last().HasAnyMeshInitialized();
}
bool HasAnyLODInitialized() const;
/// <summary>
/// Determines whether this model can be rendered.
@@ -184,22 +170,14 @@ public:
/// </summary>
/// <param name="lodIndex">Index of the LOD.</param>
/// <returns>Task that will gather chunk data or null if already here.</returns>
ContentLoadTask* RequestLODDataAsync(int32 lodIndex)
{
const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
return RequestChunkDataAsync(chunkIndex);
}
ContentLoadTask* RequestLODDataAsync(int32 lodIndex);
/// <summary>
/// Gets the model LOD data (links bytes).
/// </summary>
/// <param name="lodIndex">Index of the LOD.</param>
/// <param name="data">The data (may be missing if failed to get it).</param>
void GetLODData(int32 lodIndex, BytesContainer& data) const
{
const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex);
GetChunkData(chunkIndex, data);
}
void GetLODData(int32 lodIndex, BytesContainer& data) const;
public:
@@ -300,18 +278,11 @@ public:
// [ModelBase]
void SetupMaterialSlots(int32 slotsCount) override;
int32 GetLODsCount() const override;
void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) override;
void InitAsVirtual() override;
#if USE_EDITOR
void GetReferences(Array<Guid>& output) const override
{
// Base
BinaryAsset::GetReferences(output);
for (int32 i = 0; i < MaterialSlots.Count(); i++)
{
output.Add(MaterialSlots[i].Material.GetID());
}
}
void GetReferences(Array<Guid>& output) const override;
#endif
// [StreamableResource]

View File

@@ -10,7 +10,7 @@
#include "Engine/Scripting/MainThreadManagedInvokeAction.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
REGISTER_BINARY_ASSET(Texture, "FlaxEngine.Texture", ::New<TextureAssetUpgrader>(), true);
REGISTER_BINARY_ASSET_WITH_UPGRADER(Texture, "FlaxEngine.Texture", TextureAssetUpgrader, true);
Texture::Texture(const SpawnParams& params, const AssetInfo* info)
: TextureBase(params, info)

View File

@@ -17,6 +17,7 @@
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Serialization/JsonWriter.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Utilities/StringConverter.h"
#include "FlaxEngine.Gen.h"
@@ -1276,7 +1277,7 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
}
}
REGISTER_BINARY_ASSET(VisualScript, "FlaxEngine.VisualScript", nullptr, false);
REGISTER_BINARY_ASSET(VisualScript, "FlaxEngine.VisualScript", false);
VisualScript::VisualScript(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)
@@ -1329,6 +1330,7 @@ Asset::LoadResult VisualScript::load()
{
case GRAPH_NODE_MAKE_TYPE(16, 3):
{
// Override method
auto& method = _methods.AddOne();
method.Script = this;
method.Node = &node;
@@ -1342,6 +1344,7 @@ Asset::LoadResult VisualScript::load()
}
case GRAPH_NODE_MAKE_TYPE(16, 6):
{
// Function
auto& method = _methods.AddOne();
method.Script = this;
method.Node = &node;
@@ -1380,6 +1383,17 @@ Asset::LoadResult VisualScript::load()
}
}
}
#if COMPILE_WITH_PROFILER
for (auto& method : _methods)
{
const StringView assetName(StringUtils::GetFileNameWithoutExtension(GetPath()));
method.ProfilerName.Resize(assetName.Length() + 2 + method.Name.Length());
StringUtils::ConvertUTF162ANSI(assetName.Get(), method.ProfilerName.Get(), assetName.Length());
method.ProfilerName.Get()[assetName.Length()] = ':';
method.ProfilerName.Get()[assetName.Length() + 1] = ':';
Platform::MemoryCopy(method.ProfilerName.Get() + assetName.Length() + 2, method.Name.Get(), method.Name.Length());
}
#endif
// Setup fields list
_fields.Resize(Graph.Parameters.Count());
@@ -2132,7 +2146,7 @@ BytesContainer VisualScript::LoadSurface()
return result;
}
LOG(Warning, "\'{0}\' surface data is missing.", GetPath());
LOG(Warning, "\'{0}\' surface data is missing.", ToString());
return BytesContainer();
}
@@ -2284,6 +2298,7 @@ VisualScriptingBinaryModule* VisualScripting::GetBinaryModule()
Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters)
{
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
PROFILE_CPU_NAMED(*method->ProfilerName);
// Add to the calling stack
ScopeContext scope;

View File

@@ -115,6 +115,9 @@ public:
MethodFlags MethodFlags;
ScriptingTypeMethodSignature Signature;
Array<StringAnsi, InlinedAllocation<16>> ParamNames;
#if COMPILE_WITH_PROFILER
StringAnsi ProfilerName;
#endif
};
struct Field

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "BinaryAsset.h"
#include "Cache/AssetsCache.h"
#include "Storage/ContentStorageManager.h"
#include "Loading/Tasks/LoadAssetDataTask.h"
#include "Engine/ContentImporters/AssetsImportingManager.h"
@@ -131,6 +132,13 @@ void BinaryAsset::GetImportMetadata(String& path, String& username) const
}
}
String BinaryAsset::GetImportPath() const
{
String path, username;
GetImportMetadata(path, username);
return path;
}
void BinaryAsset::ClearDependencies()
{
for (auto& e : Dependencies)
@@ -293,7 +301,12 @@ bool BinaryAsset::LoadChunks(AssetChunksFlag chunks)
#if USE_EDITOR
bool BinaryAsset::SaveAsset(const StringView& path, AssetInitData& data, bool silentMode)
bool BinaryAsset::SaveAsset(AssetInitData& data, bool silentMode) const
{
return SaveAsset(GetPath(), data, silentMode);
}
bool BinaryAsset::SaveAsset(const StringView& path, AssetInitData& data, bool silentMode) const
{
data.Header = _header;
data.Metadata.Link(Metadata);
@@ -303,9 +316,13 @@ bool BinaryAsset::SaveAsset(const StringView& path, AssetInitData& data, bool si
bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool silentMode)
{
// Ensure path is in a valid format
String pathNorm(path);
FileSystem::NormalizePath(pathNorm);
// Find target storage container and the asset
auto storage = ContentStorageManager::TryGetStorage(path);
auto asset = Content::GetAsset(path);
auto storage = ContentStorageManager::TryGetStorage(pathNorm);
auto asset = Content::GetAsset(pathNorm);
auto binaryAsset = dynamic_cast<BinaryAsset*>(asset);
if (asset && !binaryAsset)
{
@@ -351,8 +368,8 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool
}
else
{
ASSERT(path.HasChars());
result = FlaxStorage::Create(path, data, silentMode);
ASSERT(pathNorm.HasChars());
result = FlaxStorage::Create(pathNorm, data, silentMode);
}
if (binaryAsset)
binaryAsset->_isSaving = false;
@@ -429,7 +446,12 @@ void BinaryAsset::OnDeleteObject()
const String& BinaryAsset::GetPath() const
{
#if USE_EDITOR
return Storage ? Storage->GetPath() : String::Empty;
#else
// In build all assets are packed into packages so use ID for original path lookup
return Content::GetRegistry()->GetEditorAssetPath(_id);
#endif
}
/// <summary>
@@ -487,7 +509,6 @@ protected:
return Result::Ok;
}
void OnEnd() override
{
_dataLock.Release();

View File

@@ -109,13 +109,7 @@ public:
/// <summary>
/// Gets the imported file path from the asset metadata (can be empty if not available).
/// </summary>
/// <returns>The imported source file path.</returns>
API_PROPERTY() String GetImportPath() const
{
String path, username;
GetImportMetadata(path, username);
return path;
}
API_PROPERTY() String GetImportPath() const;
/// <summary>
/// Clears the asset dependencies list and unregisters from tracking their changes.
@@ -131,7 +125,6 @@ public:
/// <summary>
/// Determines whether any of the dependency assets was modified after last modification time of this asset (last file write time check).
/// </summary>
/// <returns><c>true</c> if one or more dependencies were modified; otherwise, <c>false</c>.</returns>
bool HasDependenciesModified() const;
protected:
@@ -277,10 +270,7 @@ public:
/// <param name="data">Asset data.</param>
/// <param name="silentMode">In silent mode don't reload opened storage container that is using target file.</param>
/// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool SaveAsset(AssetInitData& data, bool silentMode = false)
{
return SaveAsset(GetPath(), data, silentMode);
}
bool SaveAsset(AssetInitData& data, bool silentMode = false) const;
/// <summary>
/// Saves this asset to the file.
@@ -289,7 +279,7 @@ public:
/// <param name="path">Asset path (will be used to override the asset or create a new one).</param>
/// <param name="silentMode">In silent mode don't reload opened storage container that is using target file.</param>
/// <returns>True if failed, otherwise false.</returns>
bool SaveAsset(const StringView& path, AssetInitData& data, bool silentMode = false);
bool SaveAsset(const StringView& path, AssetInitData& data, bool silentMode = false) const;
/// <summary>
/// Saves asset data to the storage container. Asset unique ID is handled by auto.

View File

@@ -3,6 +3,7 @@
#include "AssetsCache.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/DeleteMe.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Serialization/FileReadStream.h"
@@ -10,12 +11,12 @@
#include "Engine/Content/Storage/ContentStorageManager.h"
#include "Engine/Content/Storage/JsonStorageProxy.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Engine/Globals.h"
#include "FlaxEngine.Gen.h"
AssetsCache::AssetsCache()
: _isDirty(false)
, _registry(4096)
, _pathsMapping(256)
{
}
@@ -91,8 +92,8 @@ void AssetsCache::Init()
#if ENABLE_ASSETS_DISCOVERY
stream->Read(&e.FileModified);
#else
DateTime tmp1;
stream->Read(&tmp1);
DateTime tmp1;
stream->Read(&tmp1);
#endif
if (flags & AssetsCacheFlags::RelativePaths && e.Info.Path.HasChars())
@@ -207,7 +208,7 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa
#if ENABLE_ASSETS_DISCOVERY
stream->Write(&e.FileModified);
#else
stream->WriteInt64(0);
stream->WriteInt64(0);
#endif
index++;
@@ -231,6 +232,21 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa
return false;
}
const String& AssetsCache::GetEditorAssetPath(const Guid& id) const
{
#if USE_EDITOR
auto e = _registry.TryGet(id);
return e ? e->Info.Path : String::Empty;
#else
for (auto& e : _pathsMapping)
{
if (e.Value == id)
return e.Key;
}
return String::Empty;
#endif
}
bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
{
PROFILE_CPU();
@@ -402,41 +418,55 @@ void AssetsCache::RegisterAssets(FlaxStorage* storage)
_isDirty = true;
}
void AssetsCache::RegisterAsset(const AssetHeader& header, const StringView& path)
{
RegisterAsset(header.ID, header.TypeName, path);
}
void AssetsCache::RegisterAssets(const FlaxStorageReference& storage)
{
RegisterAssets(storage.Get());
}
void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const StringView& path)
{
PROFILE_CPU();
ScopeLock lock(_locker);
// Mark registry as draft
_isDirty = true;
// Check if asset has been already added to the registry
bool isMissing = true;
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
{
auto& e = i->Value;
// Compare IDs
if (e.Info.ID == id)
{
// Update registry entry
e.Info.Path = path;
e.Info.TypeName = typeName;
// Back
if (e.Info.Path != path)
{
e.Info.Path = path;
_isDirty = true;
}
if (e.Info.TypeName != typeName)
{
e.Info.TypeName = typeName;
_isDirty = true;
}
isMissing = false;
break;
}
// Compare paths
if (e.Info.Path == path)
{
// Update registry entry
e.Info.ID = id;
e.Info.TypeName = typeName;
// Back
if (e.Info.ID != id)
{
e.Info.Path = path;
_isDirty = true;
}
if (e.Info.TypeName != typeName)
{
e.Info.TypeName = typeName;
_isDirty = true;
}
isMissing = false;
break;
}
@@ -445,9 +475,8 @@ void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const St
if (isMissing)
{
LOG(Info, "Register asset {0}:{1} \'{2}\'", id, typeName, path);
// Add new asset entry
_registry.Add(id, Entry(id, typeName, path));
_isDirty = true;
}
}
@@ -567,8 +596,8 @@ bool AssetsCache::IsEntryValid(Entry& e)
#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();
// Skip only entries with missing file
return e.Info.Path.HasChars();
#endif
}

View File

@@ -2,13 +2,19 @@
#pragma once
#include "../AssetInfo.h"
#include "../Config.h"
#include "Engine/Core/Types/Guid.h"
#if ENABLE_ASSETS_DISCOVERY
#include "Engine/Core/Types/DateTime.h"
#endif
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Platform/CriticalSection.h"
#include "Engine/Content/Storage/FlaxStorageReference.h"
#include "../AssetInfo.h"
#include "../Config.h"
struct AssetHeader;
struct FlaxStorageReference;
class FlaxStorage;
/// <summary>
/// Assets cache flags.
@@ -31,7 +37,7 @@ DECLARE_ENUM_OPERATORS(AssetsCacheFlags);
/// <summary>
/// Flax Game Engine assets cache container
/// </summary>
class AssetsCache
class FLAXENGINE_API AssetsCache
{
public:
@@ -46,19 +52,17 @@ public:
AssetInfo Info;
#if ENABLE_ASSETS_DISCOVERY
/// <summary>
/// The file modified date.
/// </summary>
DateTime FileModified;
#endif
Entry()
{
}
Entry(const Guid& id, const String& typeName, const StringView& path)
Entry(const Guid& id, const StringView& typeName, const StringView& path)
: Info(id, typeName, path)
#if ENABLE_ASSETS_DISCOVERY
, FileModified(DateTime::NowUTC())
@@ -124,6 +128,13 @@ public:
public:
/// <summary>
/// Finds the asset path by id. In editor it returns the actual asset path, at runtime it returns the mapped asset path.
/// </summary>
/// <param name="id">The asset id.</param>
/// <returns>The asset path, or empty if failed to find.</returns>
const String& GetEditorAssetPath(const Guid& id) const;
/// <summary>
/// Finds the asset info by path.
/// </summary>
@@ -167,16 +178,13 @@ public:
/// </summary>
/// <param name="typeName">The asset typename.</param>
/// <param name="result">The result array.</param>
void GetAllByTypeName(const StringView& typeName, Array<Guid>& result) const;
void GetAllByTypeName(const StringView& typeName, Array<Guid, HeapAllocation>& result) const;
/// <summary>
/// Register assets in the cache
/// </summary>
/// <param name="storage">Flax assets container reference</param>
FORCE_INLINE void RegisterAssets(const FlaxStorageReference& storage)
{
RegisterAssets(storage.Get());
}
void RegisterAssets(const FlaxStorageReference& storage);
/// <summary>
/// Register assets in the cache
@@ -189,10 +197,7 @@ public:
/// </summary>
/// <param name="header">Flax asset file header</param>
/// <param name="path">Asset path</param>
FORCE_INLINE void RegisterAsset(const AssetHeader& header, const StringView& path)
{
RegisterAsset(header.ID, header.TypeName, path);
}
void RegisterAsset(const AssetHeader& header, const StringView& path);
/// <summary>
/// Register asset in the cache

View File

@@ -14,6 +14,7 @@
#include "Engine/Threading/Threading.h"
#include "Engine/Graphics/Graphics.h"
#include "Engine/Engine/Time.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Level/Types.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
@@ -408,16 +409,20 @@ Asset* Content::LoadAsync(const StringView& path, MClass* type)
Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& type)
{
// Ensure path is in a valid format
String pathNorm(path);
FileSystem::NormalizePath(pathNorm);
#if USE_EDITOR
if (!FileSystem::FileExists(path))
if (!FileSystem::FileExists(pathNorm))
{
LOG(Error, "Missing file \'{0}\'", path);
LOG(Error, "Missing file \'{0}\'", pathNorm);
return nullptr;
}
#endif
AssetInfo assetInfo;
if (GetAssetInfo(path, assetInfo))
if (GetAssetInfo(pathNorm, assetInfo))
{
return LoadAsync(assetInfo.ID, type);
}

View File

@@ -4,6 +4,7 @@
#include "../BinaryAsset.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Content/Storage/ContentStorageManager.h"
#if USE_EDITOR
@@ -14,28 +15,23 @@
bool BinaryAssetFactoryBase::Init(BinaryAsset* asset)
{
ASSERT(asset && asset->Storage);
// Prepare
auto storage = asset->Storage;
AssetInfo info;
info.ID = asset->GetID();
info.TypeName = asset->GetTypeName();
info.Path = storage->GetPath();
// Load serialized asset data
AssetInitData initData;
if (storage->LoadAssetHeader(info.ID, initData))
if (storage->LoadAssetHeader(asset->GetID(), initData))
{
LOG(Error, "Cannot load asset header.\nInfo: {0}", info.ToString());
LOG(Error, "Cannot load asset header.\nInfo: {0}", AssetInfo(asset->GetID(), asset->GetTypeName(), storage->GetPath()).ToString());
return true;
}
#if COMPILE_WITH_ASSET_UPGRADERS
#if USE_EDITOR
// Check if need to perform data conversion to the newer version (only in Editor)
const auto upgrader = GetUpgrader();
if (storage->AllowDataModifications() && upgrader && upgrader->ShouldUpgrade(initData.SerializedVersion))
{
const auto startTime = DateTime::NowUTC();
const AssetInfo info(asset->GetID(), asset->GetTypeName(), storage->GetPath());
LOG(Info, "Starting asset \'{0}\' conversion", info.Path);
// Backup source file (in case of conversion failure)
@@ -99,21 +95,21 @@ bool BinaryAssetFactoryBase::Init(BinaryAsset* asset)
// Check if serialized asset version is supported
if (!IsVersionSupported(initData.SerializedVersion))
{
LOG(Warning, "Asset version {1} is not supported.\nInfo: {0}", info.ToString(), initData.SerializedVersion);
LOG(Warning, "Asset version {1} is not supported.\nInfo: {0}", AssetInfo(asset->GetID(), asset->GetTypeName(), storage->GetPath()).ToString(), initData.SerializedVersion);
return true;
}
// Initialize asset
if (asset->Init(initData))
{
LOG(Error, "Cannot initialize asset.\nInfo: {0}", info.ToString());
LOG(Error, "Cannot initialize asset.\nInfo: {0}", AssetInfo(asset->GetID(), asset->GetTypeName(), storage->GetPath()).ToString());
return true;
}
return false;
}
#if COMPILE_WITH_ASSET_UPGRADERS
#if USE_EDITOR
bool BinaryAssetFactoryBase::UpgradeAsset(const AssetInfo& info, FlaxStorage* storage, AssetMigrationContext& context)
{

View File

@@ -3,7 +3,9 @@
#pragma once
#include "IAssetFactory.h"
#if USE_EDITOR
#include "Engine/Content/Upgraders/BinaryAssetUpgrader.h"
#endif
#include "Engine/Content/AssetInfo.h"
#include "Engine/Scripting/ScriptingObject.h"
@@ -14,7 +16,7 @@ class FlaxStorage;
/// The binary assets factory base class.
/// </summary>
/// <seealso cref="IAssetFactory" />
class BinaryAssetFactoryBase : public IAssetFactory
class FLAXENGINE_API BinaryAssetFactoryBase : public IAssetFactory
{
public:
@@ -29,7 +31,7 @@ protected:
virtual BinaryAsset* Create(const AssetInfo& info) = 0;
virtual bool IsVersionSupported(uint32 serializedVersion) const = 0;
#if COMPILE_WITH_ASSET_UPGRADERS
#if USE_EDITOR
bool UpgradeAsset(const AssetInfo& info, FlaxStorage* storage, AssetMigrationContext& context);
#endif
@@ -65,16 +67,31 @@ protected:
}
};
#define REGISTER_BINARY_ASSET(type, typeName, upgrader, supportsVirtualAssets) \
#define REGISTER_BINARY_ASSET(type, typeName, supportsVirtualAssets) \
const String type::TypeName = TEXT(typeName); \
class CONCAT_MACROS(Factory, type) : public BinaryAssetFactory<type> \
{ \
public: \
CONCAT_MACROS(Factory, type)() { IAssetFactory::Get().Add(type::TypeName, this); } \
~CONCAT_MACROS(Factory, type)() { IAssetFactory::Get().Remove(type::TypeName); } \
bool SupportsVirtualAssets() const override { return supportsVirtualAssets; } \
}; \
static CONCAT_MACROS(Factory, type) CONCAT_MACROS(CFactory, type)
#if USE_EDITOR
#define REGISTER_BINARY_ASSET_WITH_UPGRADER(type, typeName, upgrader, supportsVirtualAssets) \
const String type::TypeName = TEXT(typeName); \
class CONCAT_MACROS(Factory, type) : public BinaryAssetFactory<type> \
{ \
private: \
IAssetUpgrader* _upgrader = upgrader; \
IAssetUpgrader* _upgrader = ::New<upgrader>(); \
public: \
CONCAT_MACROS(Factory, type)() { IAssetFactory::Get().Add(type::TypeName, this); } \
~CONCAT_MACROS(Factory, type)() { if (_upgrader) Delete(_upgrader); IAssetFactory::Get().Remove(type::TypeName); } \
~CONCAT_MACROS(Factory, type)() { Delete(_upgrader); IAssetFactory::Get().Remove(type::TypeName); } \
bool SupportsVirtualAssets() const override { return supportsVirtualAssets; } \
IAssetUpgrader* GetUpgrader() const override { return _upgrader; } \
}; \
static CONCAT_MACROS(Factory, type) CONCAT_MACROS(CFactory, type)
#else
#define REGISTER_BINARY_ASSET_WITH_UPGRADER(type, typeName, upgrader, supportsVirtualAssets) REGISTER_BINARY_ASSET(type, typeName, supportsVirtualAssets)
#endif

View File

@@ -9,13 +9,10 @@ struct AssetInfo;
class Asset;
class IAssetUpgrader;
// Enables upgrading asset files from the older version format
#define COMPILE_WITH_ASSET_UPGRADERS (USE_EDITOR)
/// <summary>
/// The asset objects factory.
/// </summary>
class IAssetFactory
class FLAXENGINE_API IAssetFactory
{
public:

View File

@@ -10,7 +10,7 @@
/// The Json assets factory base class.
/// </summary>
/// <seealso cref="IAssetFactory" />
class JsonAssetFactoryBase : public IAssetFactory
class FLAXENGINE_API JsonAssetFactoryBase : public IAssetFactory
{
protected:
@@ -23,7 +23,6 @@ public:
{
return Create(info);
}
Asset* NewVirtual(const AssetInfo& info) override
{
return Create(info);
@@ -47,12 +46,13 @@ protected:
}
};
#define REGISTER_JSON_ASSET(type, typeName) \
#define REGISTER_JSON_ASSET(type, typeName, supportsVirtualAssets) \
const String type::TypeName = TEXT(typeName); \
class CONCAT_MACROS(Factory, type) : public JsonAssetFactory<type> \
{ \
public: \
CONCAT_MACROS(Factory, type)() { IAssetFactory::Get().Add(type::TypeName, this); } \
~CONCAT_MACROS(Factory, type)() { IAssetFactory::Get().Remove(type::TypeName); } \
bool SupportsVirtualAssets() const override { return supportsVirtualAssets; } \
}; \
static CONCAT_MACROS(Factory, type) CONCAT_MACROS(CFactory, type)

View File

@@ -1,12 +1,16 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "JsonAsset.h"
#include "Storage/ContentStorageManager.h"
#include "Engine/Threading/Threading.h"
#if USE_EDITOR
#include "Engine/Platform/File.h"
#include "Engine/Core/Types/DataContainer.h"
#else
#include "Storage/ContentStorageManager.h"
#endif
#include "Content.h"
#include "FlaxEngine.Gen.h"
#include "Cache/AssetsCache.h"
#include "Engine/Core/Log.h"
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Content/Factories/JsonAssetFactory.h"
@@ -38,7 +42,12 @@ String JsonAssetBase::GetData() const
const String& JsonAssetBase::GetPath() const
{
#if USE_EDITOR
return _path;
#else
// In build all assets are packed into packages so use ID for original path lookup
return Content::GetRegistry()->GetEditorAssetPath(_id);
#endif
}
#if USE_EDITOR
@@ -93,9 +102,8 @@ Asset::LoadResult JsonAssetBase::loadAsset()
{
// Load data (raw json file in editor, cooked asset in build game)
#if USE_EDITOR
BytesContainer data;
if (File::ReadAllBytes(GetPath(), data))
if (File::ReadAllBytes(_path, data))
{
LOG(Warning, "Filed to load json asset data. {0}", ToString());
return LoadResult::CannotLoadData;
@@ -104,11 +112,9 @@ Asset::LoadResult JsonAssetBase::loadAsset()
{
return LoadResult::MissingDataChunk;
}
#else
// Get the asset storage container but don't load it now
const auto storage = ContentStorageManager::GetStorage(GetPath(), true);
const auto storage = ContentStorageManager::GetStorage(_path, true);
if (!storage)
return LoadResult::CannotLoadStorage;
@@ -124,7 +130,6 @@ Asset::LoadResult JsonAssetBase::loadAsset()
if (storage->LoadAssetChunk(chunk))
return LoadResult::CannotLoadData;
auto& data = chunk->Data;
#endif
// Parse json document
@@ -176,7 +181,7 @@ void JsonAssetBase::onRename(const StringView& newPath)
#endif
REGISTER_JSON_ASSET(JsonAsset, "FlaxEngine.JsonAsset");
REGISTER_JSON_ASSET(JsonAsset, "FlaxEngine.JsonAsset", true);
JsonAsset::JsonAsset(const SpawnParams& params, const AssetInfo* info)
: JsonAssetBase(params, info)

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Threading/Task.h"
#include "Engine/Core/Types/String.h"
class Asset;
class LoadingThread;

View File

@@ -165,7 +165,7 @@ bool ContentLoadingManagerService::Init()
// Calculate amount of loading threads to use
const CPUInfo cpuInfo = Platform::GetCPUInfo();
const int32 count = static_cast<int32>(Math::Clamp(LOADING_THREAD_PER_PHYSICAL_CORE * cpuInfo.ProcessorCoreCount, 1.0f, 4.0f));
const int32 count = Math::Clamp(static_cast<int32>(LOADING_THREAD_PER_PHYSICAL_CORE * (float)cpuInfo.ProcessorCoreCount), 1, 6);
LOG(Info, "Creating {0} content loading threads...", count);
// Create loading threads

View File

@@ -53,6 +53,9 @@ protected:
AssetReference<BinaryAsset> ref = _asset.Get();
if (ref == nullptr)
return Result::MissingReferences;
#if TRACY_ENABLE
const StringView name(ref->GetPath());
#endif
// Load chunks
for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++)
@@ -62,11 +65,14 @@ protected:
const auto chunk = ref->GetChunk(i);
if (chunk != nullptr)
{
// Check for cancel
if (IsCancelRequested())
return Result::Ok;
// Load it
#if TRACY_ENABLE
ZoneScoped;
ZoneName(*name, name.Length());
#endif
if (ref->Storage->LoadAssetChunk(chunk))
{
LOG(Warning, "Cannot load asset \'{0}\' chunk {1}.", ref->ToString(), i);

View File

@@ -13,10 +13,6 @@
/// </summary>
class LoadAssetTask : public ContentLoadTask
{
private:
WeakAssetReference<Asset> _asset;
public:
/// <summary>
@@ -25,27 +21,20 @@ public:
/// <param name="asset">The asset to load.</param>
LoadAssetTask(Asset* asset)
: ContentLoadTask(Type::LoadAsset)
, _asset(asset)
, Asset(asset)
{
}
public:
/// <summary>
/// Gets the asset.
/// </summary>
/// <returns>The asset.</returns>
FORCE_INLINE Asset* GetAsset() const
{
return _asset.Get();
}
WeakAssetReference<Asset> Asset;
public:
// [ContentLoadTask]
bool HasReference(Object* obj) const override
{
return obj == _asset;
return obj == Asset;
}
protected:
@@ -55,7 +44,8 @@ protected:
{
PROFILE_CPU();
AssetReference<Asset> ref = _asset.Get();
// Keep valid ref to the asset
AssetReference<::Asset> ref = Asset.Get();
if (ref == nullptr)
return Result::MissingReferences;
@@ -68,7 +58,7 @@ protected:
void OnEnd() override
{
_asset = nullptr;
Asset = nullptr;
// Base
ContentLoadTask::OnEnd();

View File

@@ -5,7 +5,9 @@
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/Pair.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/DataContainer.h"
#if USE_EDITOR
#include "Engine/Core/Collections/Array.h"
#endif
#include "FlaxChunk.h"
/// <summary>
@@ -54,7 +56,8 @@ public:
/// Gets the chunks.
/// </summary>
/// <param name="output">The output data.</param>
void GetChunks(Array<FlaxChunk*>& output) const
template<typename AllocationType = HeapAllocation>
void GetChunks(Array<FlaxChunk*, AllocationType>& output) const
{
for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++)
{
@@ -67,7 +70,8 @@ public:
/// Gets the chunks that are loaded.
/// </summary>
/// <param name="output">The output data.</param>
void GetLoadedChunks(Array<FlaxChunk*>& output) const
template<typename AllocationType = HeapAllocation>
void GetLoadedChunks(Array<FlaxChunk*, AllocationType>& output) const
{
for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++)
{
@@ -130,7 +134,7 @@ struct FLAXENGINE_API AssetInitData
/// <summary>
/// The serialized asset version
/// </summary>
uint32 SerializedVersion;
uint32 SerializedVersion = 0;
/// <summary>
/// The custom asset data (should be small, for eg. texture description structure).
@@ -138,7 +142,6 @@ struct FLAXENGINE_API AssetInitData
BytesContainer CustomData;
#if USE_EDITOR
/// <summary>
/// The asset metadata information. Stored in a Json format.
/// </summary>
@@ -148,24 +151,16 @@ struct FLAXENGINE_API AssetInitData
/// Asset dependencies list used by the asset for tracking (eg. material functions used by material asset). The pair of asset ID and cached file edit time (for tracking modification).
/// </summary>
Array<Pair<Guid, DateTime>> Dependencies;
#endif
public:
AssetInitData()
: SerializedVersion(0)
{
}
/// <summary>
/// Gets the hash code.
/// </summary>
/// <returns>Hash Code</returns>
uint32 GetHashCode() const
{
// Note: do not use Metadata/Dependencies because it may not be loaded (it's optional)
uint32 hashCode = GetHash(Header.ID);
hashCode = (hashCode * 397) ^ SerializedVersion;
hashCode = (hashCode * 397) ^ CustomData.Length();

View File

@@ -3,6 +3,7 @@
#pragma once
#include "FlaxStorageReference.h"
#include "Engine/Core/Types/TimeSpan.h"
class FlaxFile;
class FlaxPackage;

View File

@@ -5,10 +5,16 @@
#include "FlaxPackage.h"
#include "ContentStorageManager.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Platform/File.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Serialization/FileWriteStream.h"
#if USE_EDITOR
#include "Engine/Serialization/JsonWriter.h"
#include "Engine/Serialization/JsonWriters.h"
#else
#include "Engine/Engine/Globals.h"
#endif
#include <ThirdParty/LZ4/lz4.h>
String AssetHeader::ToString() const
@@ -194,13 +200,13 @@ void FlaxStorage::AddRef()
void FlaxStorage::RemoveRef()
{
ASSERT(_refCount > 0);
_refCount--;
if (_refCount == 0)
if (_refCount > 0)
{
_lastRefLostTime = DateTime::NowUTC();
_refCount--;
if (_refCount == 0)
{
_lastRefLostTime = DateTime::NowUTC();
}
}
}
@@ -623,6 +629,7 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
stream->ReadBytes(tmpBuf.Get(), size);
// Decompress data
PROFILE_CPU_NAMED("DecompressLZ4");
chunk->Data.Allocate(originalSize);
const int32 res = LZ4_decompress_safe((const char*)tmpBuf.Get(), chunk->Data.Get<char>(), size, originalSize);
if (res <= 0)
@@ -823,6 +830,7 @@ bool FlaxStorage::Create(WriteStream* stream, const AssetInitData* data, int32 d
const FlaxChunk* chunk = chunks[i];
if (chunk->Flags & FlaxChunkFlags::CompressedLZ4)
{
PROFILE_CPU_NAMED("CompressLZ4");
const int32 srcSize = chunk->Data.Length();
const int32 maxSize = LZ4_compressBound(srcSize);
auto& chunkCompressed = compressedChunks[i];

View File

@@ -5,7 +5,7 @@
#include "FlaxStorage.h"
/// <summary>
/// Flax Storage Container Reference
/// Flax Storage container reference.
/// </summary>
struct FLAXENGINE_API FlaxStorageReference
{
@@ -44,10 +44,8 @@ public:
public:
// Assignment operator
FlaxStorageReference& operator=(const FlaxStorageReference& other)
{
// Protect against invalid self-assignment
if (this != &other)
{
if (_storage)
@@ -56,7 +54,6 @@ public:
if (_storage)
_storage->AddRef();
}
return *this;
}

View File

@@ -31,7 +31,7 @@ bool JsonStorageProxy::GetAssetInfo(const StringView& path, Guid& resultId, Stri
document.Parse((const char*)fileData.Get(), fileData.Count());
if (document.HasParseError())
{
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset(), String(path));
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset(), path);
return false;
}
@@ -94,7 +94,7 @@ bool JsonStorageProxy::ChangeId(const StringView& path, const Guid& newId)
document.Parse((const char*)fileData.Get(), fileData.Count());
if (document.HasParseError())
{
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset(), String(path));
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset(), path);
return false;
}

View File

@@ -2,6 +2,8 @@
#pragma once
#if USE_EDITOR
#include "BinaryAssetUpgrader.h"
#include "Engine/Audio/AudioClip.h"
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
@@ -130,3 +132,5 @@ private:
return false;
}
};
#endif

View File

@@ -2,6 +2,8 @@
#pragma once
#if USE_EDITOR
#include "IAssetUpgrader.h"
#include "Engine/Content/Storage/AssetHeader.h"
#include "Engine/Core/Log.h"
@@ -9,7 +11,7 @@
/// <summary>
/// Binary asset upgrading context structure.
/// </summary>
struct AssetMigrationContext
struct FLAXENGINE_API AssetMigrationContext
{
/// <summary>
/// The input data.
@@ -63,7 +65,7 @@ typedef bool (*UpgradeHandler)(AssetMigrationContext& context);
/// Binary Assets Upgrader base class
/// </summary>
/// <seealso cref="IAssetUpgrader" />
class BinaryAssetUpgrader : public IAssetUpgrader
class FLAXENGINE_API BinaryAssetUpgrader : public IAssetUpgrader
{
public:
@@ -205,3 +207,5 @@ public:
return false;
}
};
#endif

View File

@@ -2,6 +2,8 @@
#pragma once
#if USE_EDITOR
#include "BinaryAssetUpgrader.h"
#include "Engine/Render2D/FontAsset.h"
@@ -71,3 +73,5 @@ private:
return CopyChunk(context, 0);
}
};
#endif

View File

@@ -2,12 +2,14 @@
#pragma once
#if USE_EDITOR
#include "Engine/Core/Types/BaseTypes.h"
/// <summary>
/// The assets upgrading objects interface.
/// </summary>
class IAssetUpgrader
class FLAXENGINE_API IAssetUpgrader
{
public:
@@ -27,3 +29,5 @@ public:
/// <returns>True if perform conversion, otherwise false.</returns>
virtual bool ShouldUpgrade(uint32 serializedVersion) const = 0;
};
#endif

View File

@@ -2,6 +2,8 @@
#pragma once
#if USE_EDITOR
#include "BinaryAssetUpgrader.h"
#include "Engine/Core/Core.h"
#include "Engine/Platform/Platform.h"
@@ -105,3 +107,5 @@ private:
return false;
}
};
#endif

View File

@@ -2,12 +2,15 @@
#pragma once
#if USE_EDITOR
#include "BinaryAssetUpgrader.h"
#include "Engine/Core/Core.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Graphics/Models/ModelData.h"
#include "Engine/Content/Asset.h"
/// <summary>
/// Model Asset Upgrader
@@ -1222,3 +1225,5 @@ private:
return false;
}
};
#endif

View File

@@ -2,8 +2,11 @@
#pragma once
#if USE_EDITOR
#include "BinaryAssetUpgrader.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Graphics/Shaders/Cache/ShaderStorage.h"
/// <summary>
/// Material Asset and Shader Asset Upgrader
@@ -85,3 +88,5 @@ private:
return CopyChunks(context);
}
};
#endif

View File

@@ -2,6 +2,8 @@
#pragma once
#if USE_EDITOR
#include "BinaryAssetUpgrader.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Serialization/MemoryReadStream.h"
@@ -97,3 +99,5 @@ private:
return false;
}
};
#endif

View File

@@ -2,10 +2,17 @@
#pragma once
#if USE_EDITOR
#include "BinaryAssetUpgrader.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Graphics/Models/Types.h"
#include "Engine/Core/Math/BoundingBox.h"
#include "Engine/Core/Math/BoundingSphere.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Math/Transform.h"
/// <summary>
/// Skinned Model Asset Upgrader
@@ -418,3 +425,5 @@ private:
return false;
}
};
#endif

View File

@@ -2,6 +2,8 @@
#pragma once
#if USE_EDITOR
#include "BinaryAssetUpgrader.h"
#include "Engine/Core/Core.h"
#include "Engine/Platform/Platform.h"
@@ -825,3 +827,5 @@ private:
return true;
}
};
#endif

View File

@@ -229,3 +229,9 @@ public:
OnSet(asset);
}
};
template<typename T>
uint32 GetHash(const WeakAssetReference<T>& key)
{
return GetHash(key.GetID());
}

View File

@@ -31,6 +31,7 @@
#include "CreateParticleEmitterFunction.h"
#include "CreateAnimationGraphFunction.h"
#include "CreateVisualScript.h"
#include "CreateJson.h"
// Tags used to detect asset creation mode
const String AssetsImportingManager::CreateTextureTag(TEXT("Texture"));
@@ -91,6 +92,10 @@ CreateAssetResult CreateAssetContext::Run(const CreateAssetFunction& callback)
if (result != CreateAssetResult::Ok)
return result;
// Skip for non-flax assets (eg. json resource or custom asset type)
if (!TargetAssetPath.EndsWith(ASSET_FILES_EXTENSION))
return CreateAssetResult::Ok;
// Validate assigned TypeID
if (Data.Header.TypeName.IsEmpty())
{
@@ -112,8 +117,7 @@ CreateAssetResult CreateAssetContext::Run(const CreateAssetFunction& callback)
}
// Save file
result = Save();
result = FlaxStorage::Create(OutputPath, Data) ? CreateAssetResult::CannotSaveFile : CreateAssetResult::Ok;
if (result == CreateAssetResult::Ok)
{
_applyChangesResult = CreateAssetResult::Abort;
@@ -161,11 +165,6 @@ void CreateAssetContext::AddMeta(JsonWriter& writer) const
writer.String(Platform::GetUserName());
}
CreateAssetResult CreateAssetContext::Save()
{
return FlaxStorage::Create(OutputPath, Data) ? CreateAssetResult::CannotSaveFile : CreateAssetResult::Ok;
}
void CreateAssetContext::ApplyChanges()
{
// Get access
@@ -231,8 +230,6 @@ bool AssetsImportingManager::Create(const String& tag, const StringView& outputP
bool AssetsImportingManager::Import(const StringView& inputPath, const StringView& outputPath, Guid& assetId, void* arg)
{
ASSERT(outputPath.EndsWith(StringView(ASSET_FILES_EXTENSION)));
LOG(Info, "Importing file '{0}' to '{1}'...", inputPath, outputPath);
// Check if input file exists
@@ -246,8 +243,7 @@ bool AssetsImportingManager::Import(const StringView& inputPath, const StringVie
const String extension = FileSystem::GetExtension(inputPath).ToLower();
// Special case for raw assets
const String assetExtension = ASSET_FILES_EXTENSION;
if (assetExtension.Compare(extension, StringSearchCase::IgnoreCase) == 0)
if (StringView(ASSET_FILES_EXTENSION).Compare(StringView(extension), StringSearchCase::IgnoreCase) == 0)
{
// Simply copy file (content layer will resolve duplicated IDs, etc.)
return FileSystem::CopyFile(outputPath, inputPath);
@@ -266,8 +262,6 @@ bool AssetsImportingManager::Import(const StringView& inputPath, const StringVie
bool AssetsImportingManager::ImportIfEdited(const StringView& inputPath, const StringView& outputPath, Guid& assetId, void* arg)
{
ASSERT(outputPath.EndsWith(StringView(ASSET_FILES_EXTENSION)));
// Check if asset not exists
if (!FileSystem::FileExists(outputPath))
{
@@ -338,7 +332,7 @@ bool AssetsImportingManager::Create(const Function<CreateAssetResult(CreateAsset
else
{
// Ensure that path exists
const String outputDirectory = StringUtils::GetDirectoryName(*outputPath);
const String outputDirectory = StringUtils::GetDirectoryName(outputPath);
if (FileSystem::CreateDirectory(outputDirectory))
{
LOG(Warning, "Cannot create directory '{0}'", outputDirectory);
@@ -383,64 +377,67 @@ bool AssetsImportingManagerService::Init()
AssetImporter InBuildImporters[] =
{
// Textures and Cube Textures
{ TEXT("tga"), ImportTexture::Import },
{ TEXT("dds"), ImportTexture::Import },
{ TEXT("png"), ImportTexture::Import },
{ TEXT("bmp"), ImportTexture::Import },
{ TEXT("gif"), ImportTexture::Import },
{ TEXT("tiff"), ImportTexture::Import },
{ TEXT("tif"), ImportTexture::Import },
{ TEXT("jpeg"), ImportTexture::Import },
{ TEXT("jpg"), ImportTexture::Import },
{ TEXT("hdr"), ImportTexture::Import },
{ TEXT("raw"), ImportTexture::Import },
{ TEXT("tga"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("dds"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("png"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("bmp"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("gif"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("tiff"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("tif"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("jpeg"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("jpg"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("hdr"), ASSET_FILES_EXTENSION, ImportTexture::Import },
{ TEXT("raw"), ASSET_FILES_EXTENSION, ImportTexture::Import },
// IES Profiles
{ TEXT("ies"), ImportTexture::ImportIES },
{ TEXT("ies"), ASSET_FILES_EXTENSION, ImportTexture::ImportIES },
// Shaders
{ TEXT("shader"), ImportShader::Import },
{ TEXT("shader"), ASSET_FILES_EXTENSION, ImportShader::Import },
// Audio
{ TEXT("wav"), ImportAudio::ImportWav },
{ TEXT("mp3"), ImportAudio::ImportMp3 },
{ TEXT("wav"), ASSET_FILES_EXTENSION, ImportAudio::ImportWav },
{ TEXT("mp3"), ASSET_FILES_EXTENSION, ImportAudio::ImportMp3 },
#if COMPILE_WITH_OGG_VORBIS
{ TEXT("ogg"), ImportAudio::ImportOgg },
{ TEXT("ogg"), ASSET_FILES_EXTENSION, ImportAudio::ImportOgg },
#endif
// Fonts
{ TEXT("ttf"), ImportFont::Import },
{ TEXT("otf"), ImportFont::Import },
{ TEXT("ttf"), ASSET_FILES_EXTENSION, ImportFont::Import },
{ TEXT("otf"), ASSET_FILES_EXTENSION, ImportFont::Import },
// Models
{ TEXT("obj"), ImportModelFile::Import },
{ TEXT("fbx"), ImportModelFile::Import },
{ TEXT("x"), ImportModelFile::Import },
{ TEXT("dae"), ImportModelFile::Import },
{ TEXT("gltf"), ImportModelFile::Import },
{ TEXT("glb"), ImportModelFile::Import },
{ TEXT("obj"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("fbx"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("x"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("dae"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("gltf"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("glb"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
// gettext PO files
{ TEXT("po"), TEXT("json"), CreateJson::ImportPo },
// Models (untested formats - may fail :/)
{ TEXT("blend"), ImportModelFile::Import },
{ TEXT("bvh"), ImportModelFile::Import },
{ TEXT("ase"), ImportModelFile::Import },
{ TEXT("ply"), ImportModelFile::Import },
{ TEXT("dxf"), ImportModelFile::Import },
{ TEXT("ifc"), ImportModelFile::Import },
{ TEXT("nff"), ImportModelFile::Import },
{ TEXT("smd"), ImportModelFile::Import },
{ TEXT("vta"), ImportModelFile::Import },
{ TEXT("mdl"), ImportModelFile::Import },
{ TEXT("md2"), ImportModelFile::Import },
{ TEXT("md3"), ImportModelFile::Import },
{ TEXT("md5mesh"), ImportModelFile::Import },
{ TEXT("q3o"), ImportModelFile::Import },
{ TEXT("q3s"), ImportModelFile::Import },
{ TEXT("ac"), ImportModelFile::Import },
{ TEXT("stl"), ImportModelFile::Import },
{ TEXT("lwo"), ImportModelFile::Import },
{ TEXT("lws"), ImportModelFile::Import },
{ TEXT("lxo"), ImportModelFile::Import },
{ TEXT("blend"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("bvh"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("ase"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("ply"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("dxf"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("ifc"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("nff"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("smd"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("vta"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("mdl"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("md2"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("md3"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("md5mesh"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("q3o"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("q3s"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("ac"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("stl"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("lwo"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("lws"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
{ TEXT("lxo"), ASSET_FILES_EXTENSION, ImportModelFile::Import },
};
AssetsImportingManager::Importers.Add(InBuildImporters, ARRAY_COUNT(InBuildImporters));

View File

@@ -5,11 +5,16 @@
#if COMPILE_WITH_ASSETS_IMPORTER
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Storage/JsonStorageProxy.h"
#include "Engine/Content/Cache/AssetsCache.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Engine/Localization/LocalizedStringTable.h"
#include "Engine/Utilities/TextProcessing.h"
#include "FlaxEngine.Gen.h"
bool CreateJson::Create(const StringView& path, rapidjson_flax::StringBuffer& data, const String& dataTypename)
@@ -41,6 +46,18 @@ bool CreateJson::Create(const StringView& path, StringAnsiView& data, StringAnsi
LOG(Warning, "Asset will have different type name {0} -> {1}", typeName, String(dataTypename.Get()));
}
}
else
{
const String directory = StringUtils::GetDirectoryName(path);
if (!FileSystem::DirectoryExists(directory))
{
if (FileSystem::CreateDirectory(directory))
{
LOG(Warning, "Failed to create directory");
return true;
}
}
}
rapidjson_flax::StringBuffer buffer;
@@ -76,8 +93,184 @@ bool CreateJson::Create(const StringView& path, StringAnsiView& data, StringAnsi
{
asset->Reload();
}
else
{
Content::GetRegistry()->RegisterAsset(id, String(dataTypename), path);
}
return false;
}
void FormatPoValue(String& value)
{
value.Replace(TEXT("\\n"), TEXT("\n"));
value.Replace(TEXT("%s"), TEXT("{}"));
value.Replace(TEXT("%d"), TEXT("{}"));
}
CreateAssetResult CreateJson::ImportPo(CreateAssetContext& context)
{
// Base
IMPORT_SETUP(LocalizedStringTable, 1);
// Load file (UTF-16)
String inputData;
if (File::ReadAllText(context.InputPath, inputData))
{
return CreateAssetResult::InvalidPath;
}
// Use virtual asset for data storage and serialization
AssetReference<LocalizedStringTable> asset = Content::CreateVirtualAsset<LocalizedStringTable>();
if (!asset)
return CreateAssetResult::Error;
// Parse PO format
int32 pos = 0;
int32 pluralCount = 0;
int32 lineNumber = 0;
bool fuzzy = false, hasNewContext = false;
StringView msgctxt, msgid;
String idTmp;
while (pos < inputData.Length())
{
// Read line
const int32 startPos = pos;
while (pos < inputData.Length() && inputData[pos] != '\n')
pos++;
const StringView line(&inputData[startPos], pos - startPos);
lineNumber++;
pos++;
const int32 valueStart = line.Find('\"') + 1;
const int32 valueEnd = line.FindLast('\"');
const StringView value(line.Get() + valueStart, Math::Max(valueEnd - valueStart, 0));
if (line.StartsWith(StringView(TEXT("msgid_plural"))))
{
// Plural form
}
else if (line.StartsWith(StringView(TEXT("msgid"))))
{
// Id
msgid = value;
// Reset context if already used
if (!hasNewContext)
msgctxt = StringView();
hasNewContext = false;
}
else if (line.StartsWith(StringView(TEXT("msgstr"))))
{
// String
if (msgid.HasChars())
{
// Format message
String msgstr(value);
FormatPoValue(msgstr);
// Get message id
StringView id = msgid;
if (msgctxt.HasChars())
{
idTmp = String(msgctxt) + TEXT(".") + String(msgid);
id = idTmp;
}
int32 indexStart = line.Find('[');
if (indexStart != -1 && indexStart < valueStart)
{
indexStart++;
while (indexStart < line.Length() && StringUtils::IsWhitespace(line[indexStart]))
indexStart++;
int32 indexEnd = line.Find(']');
while (indexEnd > indexStart && StringUtils::IsWhitespace(line[indexEnd - 1]))
indexEnd--;
int32 index = -1;
StringUtils::Parse(line.Get() + indexStart, (uint32)(indexEnd - indexStart), &index);
if (pluralCount <= 0)
{
LOG(Error, "Missing 'nplurals'. Cannot use plural message at line {0}", lineNumber);
return CreateAssetResult::Error;
}
if (index < 0 || index > pluralCount)
{
LOG(Error, "Invalid plural message index at line {0}", lineNumber);
return CreateAssetResult::Error;
}
// Plural message
asset->AddPluralString(id, msgstr, index);
}
else
{
// Message
asset->AddString(id, msgstr);
}
}
}
else if (line.StartsWith(StringView(TEXT("msgctxt"))))
{
// Context
msgctxt = value;
hasNewContext = true;
}
else if (line.StartsWith('\"'))
{
// Config
const Char* pluralForms = StringUtils::Find(line.Get(), TEXT("Plural-Forms"));
if (pluralForms != nullptr && pluralForms < line.Get() + line.Length() - 1)
{
// Process plural forms rule
const Char* nplurals = StringUtils::Find(pluralForms, TEXT("nplurals"));
if (nplurals && nplurals < line.Get() + line.Length())
{
while (*nplurals && *nplurals != '=')
nplurals++;
while (*nplurals && (StringUtils::IsWhitespace(*nplurals) || *nplurals == '='))
nplurals++;
const Char* npluralsStart = nplurals;
while (*nplurals && !StringUtils::IsWhitespace(*nplurals) && *nplurals != ';')
nplurals++;
StringUtils::Parse(npluralsStart, (uint32)(nplurals - npluralsStart), &pluralCount);
if (pluralCount < 0 || pluralCount > 100)
{
LOG(Error, "Invalid 'nplurals' at line {0}", lineNumber);
return CreateAssetResult::Error;
}
}
// TODO: parse plural forms rule
}
const Char* language = StringUtils::Find(line.Get(), TEXT("Language"));
if (language != nullptr && language < line.Get() + line.Length() - 1)
{
// Process language locale
while (*language && *language != ':')
language++;
language++;
while (*language && StringUtils::IsWhitespace(*language))
language++;
const Char* languageStart = language;
while (*language && !StringUtils::IsWhitespace(*language) && *language != '\\' && *language != '\"')
language++;
asset->Locale.Set(languageStart, (int32)(language - languageStart));
if (asset->Locale == TEXT("English"))
asset->Locale = TEXT("en");
if (asset->Locale.Length() > 5)
LOG(Warning, "Imported .po file uses invalid locale '{0}'", asset->Locale);
}
}
else if (line.StartsWith('#') || line.IsEmpty())
{
// Comment
const Char* fuzzyPos = StringUtils::Find(line.Get(), TEXT("fuzzy"));
fuzzy |= fuzzyPos != nullptr && fuzzyPos < line.Get() + line.Length() - 1;
}
}
if (asset->Locale.IsEmpty())
LOG(Warning, "Imported .po file has missing locale");
// Save asset
return asset->Save(context.TargetAssetPath) ? CreateAssetResult::CannotSaveFile : CreateAssetResult::Ok;
}
#endif

View File

@@ -18,6 +18,7 @@ public:
static bool Create(const StringView& path, rapidjson_flax::StringBuffer& data, const String& dataTypename);
static bool Create(const StringView& path, rapidjson_flax::StringBuffer& data, const char* dataTypename);
static bool Create(const StringView& path, StringAnsiView& data, StringAnsiView& dataTypename);
static CreateAssetResult ImportPo(CreateAssetContext& context);
};
#endif

View File

@@ -115,7 +115,8 @@ CreateAssetResult ImportModelFile::Import(CreateAssetContext& context)
// Import model file
ModelData modelData;
String errorMsg;
if (ModelTool::ImportModel(context.InputPath, modelData, options, errorMsg, StringUtils::GetDirectoryName(context.TargetAssetPath) / StringUtils::GetFileNameWithoutExtension(context.InputPath)))
String autoImportOutput = String(StringUtils::GetDirectoryName(context.TargetAssetPath)) / StringUtils::GetFileNameWithoutExtension(context.InputPath);
if (ModelTool::ImportModel(context.InputPath, modelData, options, errorMsg, autoImportOutput))
{
LOG(Error, "Cannot import model file. {0}", errorMsg);
return CreateAssetResult::Error;

View File

@@ -111,12 +111,6 @@ public:
/// <param name="writer">The json metadata writer.</param>
void AddMeta(JsonWriter& writer) const;
/// <summary>
/// Save asset file data to the hard drive
/// </summary>
/// <returns>Saving result</returns>
CreateAssetResult Save();
private:
void ApplyChanges();
@@ -130,12 +124,17 @@ struct AssetImporter
public:
/// <summary>
/// Extension of the file to import with that importer
/// Extension of the file to import with that importer (without leading dot).
/// </summary>
String FileExtension;
/// <summary>
/// Call asset importing process
/// Extension of the output file as output with that importer (without leading dot).
/// </summary>
String ResultExtension;
/// <summary>
/// Callback for the asset importing process.
/// </summary>
CreateAssetFunction Callback;
};

View File

@@ -2,6 +2,7 @@
#pragma once
#include <initializer_list>
#include "Engine/Platform/Platform.h"
#include "Engine/Core/Memory/Memory.h"
#include "Engine/Core/Memory/Allocation.h"
@@ -47,6 +48,20 @@ public:
_allocation.Allocate(capacity);
}
/// <summary>
/// Initializes a new instance of the <see cref="Array"/> class.
/// </summary>
/// <param name="initList">The initial values defined in the array.</param>
Array(std::initializer_list<T> initList)
{
_count = _capacity = (int32)initList.size();
if (_count > 0)
{
_allocation.Allocate(_count);
Memory::ConstructItems(Get(), initList.begin(), _count);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Array"/> class.
/// </summary>
@@ -123,6 +138,24 @@ public:
_allocation.Swap(other._allocation);
}
/// <summary>
/// The assignment operator that deletes the current collection of items and the copies items from the initializer list.
/// </summary>
/// <param name="initList">The other collection to copy.</param>
/// <returns>The reference to this.</returns>
Array& operator=(std::initializer_list<T> initList) noexcept
{
Memory::DestructItems(Get(), _count);
_count = _capacity = (int32)initList.size();
if (_capacity > 0)
{
_allocation.Allocate(_capacity);
Memory::ConstructItems(Get(), initList.begin(), _count);
}
return *this;
}
/// <summary>
/// The assignment operator that deletes the current collection of items and the copies items from the other array.
/// </summary>
@@ -725,7 +758,6 @@ public:
/// <summary>
/// Performs pop from stack operation (stack grows at the end of the collection).
/// </summary>
/// <returns>The item.</returns>
T Pop()
{
T item(Last());
@@ -736,19 +768,19 @@ public:
/// <summary>
/// Peeks items which is at the top of the stack (stack grows at the end of the collection).
/// </summary>
/// <returns>The item.</returns>
FORCE_INLINE T& Peek()
{
return Last();
ASSERT(_count > 0);
return Get()[_count - 1];
}
/// <summary>
/// Peeks items which is at the top of the stack (stack grows at the end of the collection).
/// </summary>
/// <returns>The item.</returns>
FORCE_INLINE const T& Peek() const
{
return Last();
ASSERT(_count > 0);
return Get()[_count - 1];
}
public:

View File

@@ -236,7 +236,7 @@ public:
return;
ASSERT(capacity >= 0);
const int32 count = preserveContents ? (_count < capacity ? _count : capacity) : 0;
_allocation.Relocate(Math::Max<ItemType>(capacity / sizeof(ItemType), 1), _count, count);
_allocation.Relocate(Math::Max<ItemType>(capacity / sizeof(ItemType), 1), _count / sizeof(ItemType), count / sizeof(ItemType));
_capacity = capacity;
_count = count;
}

View File

@@ -2,7 +2,7 @@
#pragma once
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Memory/Memory.h"
#include "Engine/Core/Collections/HashFunctions.h"
#include "Engine/Core/Collections/Config.h"
@@ -781,7 +781,8 @@ public:
/// Gets the keys collection to the output array (will contain unique items).
/// </summary>
/// <param name="result">The result.</param>
void GetKeys(Array<KeyType>& result) const
template<typename AllocationType>
void GetKeys(Array<KeyType, AllocationType>& result) const
{
for (auto i = Begin(); i.IsNotEnd(); ++i)
result.Add(i->Key);
@@ -791,7 +792,8 @@ public:
/// Gets the values collection to the output array (may contain duplicates).
/// </summary>
/// <param name="result">The result.</param>
void GetValues(Array<ValueType>& result) const
template<typename AllocationType>
void GetValues(Array<ValueType, AllocationType>& result) const
{
for (auto i = Begin(); i.IsNotEnd(); ++i)
result.Add(i->Value);

View File

@@ -17,7 +17,7 @@ public:
/// <summary>
/// Helper collection used by the sorting algorithms. Implements stack using single linear allocation with variable capacity.
/// </summary>
class SortingStack
class FLAXENGINE_API SortingStack
{
public:

View File

@@ -13,6 +13,7 @@
#include "Engine/Input/InputSettings.h"
#include "Engine/Audio/AudioSettings.h"
#include "Engine/Navigation/NavigationSettings.h"
#include "Engine/Localization/LocalizationSettings.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/JsonAsset.h"
#include "Engine/Content/AssetReference.h"
@@ -100,7 +101,12 @@ bool GameSettings::Load()
auto settings = Get();
if (!settings)
{
#if USE_EDITOR
// Allow lack of Game Settings in Editor
return false;
#else
return true;
#endif
}
// Preload all settings assets
@@ -122,6 +128,7 @@ bool GameSettings::Load()
PRELOAD_SETTINGS(Input);
PRELOAD_SETTINGS(Graphics);
PRELOAD_SETTINGS(Navigation);
PRELOAD_SETTINGS(Localization);
PRELOAD_SETTINGS(GameCooking);
#undef PRELOAD_SETTINGS
@@ -153,6 +160,7 @@ void GameSettings::Apply()
APPLY_SETTINGS(InputSettings);
APPLY_SETTINGS(GraphicsSettings);
APPLY_SETTINGS(NavigationSettings);
APPLY_SETTINGS(LocalizationSettings);
APPLY_SETTINGS(BuildSettings);
APPLY_SETTINGS(PlatformSettings);
#undef APPLY_SETTINGS
@@ -192,6 +200,7 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
DESERIALIZE(Input);
DESERIALIZE(Graphics);
DESERIALIZE(Navigation);
DESERIALIZE(Localization);
DESERIALIZE(GameCooking);
// Per-platform settings containers

View File

@@ -79,6 +79,12 @@ namespace FlaxEditor.Content.Settings
[EditorOrder(1045), EditorDisplay("Other Settings"), AssetReference(typeof(NavigationSettings), true), Tooltip("Reference to Navigation Settings asset")]
public JsonAsset Navigation;
/// <summary>
/// Reference to <see cref="LocalizationSettings"/> asset.
/// </summary>
[EditorOrder(1046), EditorDisplay("Other Settings"), AssetReference(typeof(LocalizationSettings), true), Tooltip("Reference to Localization Settings asset")]
public JsonAsset Localization;
/// <summary>
/// Reference to <see cref="BuildSettings"/> asset.
/// </summary>
@@ -219,6 +225,8 @@ namespace FlaxEditor.Content.Settings
return LoadAsset<GraphicsSettings>(gameSettings.Graphics) as T;
if (type == typeof(NavigationSettings))
return LoadAsset<NavigationSettings>(gameSettings.Navigation) as T;
if (type == typeof(LocalizationSettings))
return LoadAsset<LocalizationSettings>(gameSettings.Localization) as T;
if (type == typeof(BuildSettings))
return LoadAsset<BuildSettings>(gameSettings.GameCooking) as T;
if (type == typeof(InputSettings))
@@ -321,6 +329,8 @@ namespace FlaxEditor.Content.Settings
return SaveAsset(gameSettings, ref gameSettings.Graphics, obj);
if (type == typeof(NavigationSettings))
return SaveAsset(gameSettings, ref gameSettings.Navigation, obj);
if (type == typeof(LocalizationSettings))
return SaveAsset(gameSettings, ref gameSettings.Localization, obj);
if (type == typeof(BuildSettings))
return SaveAsset(gameSettings, ref gameSettings.GameCooking, obj);
if (type == typeof(InputSettings))

View File

@@ -68,6 +68,7 @@ public:
Guid Input;
Guid Graphics;
Guid Navigation;
Guid Localization;
Guid GameCooking;
// Per-platform settings containers

View File

@@ -3,6 +3,8 @@
#pragma once
#include "Engine/Core/Config/Settings.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Collections/Array.h"
/// <summary>
/// Layers and objects tags settings.

View File

@@ -15,31 +15,31 @@ public:
/// <summary>
/// The target amount of the game logic updates per second (script updates frequency).
/// </summary>
API_FIELD(Attributes="EditorOrder(1), DefaultValue(30.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Update FPS\")")
float UpdateFPS = 30.0f;
API_FIELD(Attributes="EditorOrder(1), Limit(0, 1000), EditorDisplay(\"General\", \"Update FPS\")")
float UpdateFPS = 60.0f;
/// <summary>
/// The target amount of the physics simulation updates per second (also fixed updates frequency).
/// </summary>
API_FIELD(Attributes="EditorOrder(2), DefaultValue(60.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Physics FPS\")")
API_FIELD(Attributes="EditorOrder(2), Limit(0, 1000), EditorDisplay(\"General\", \"Physics FPS\")")
float PhysicsFPS = 60.0f;
/// <summary>
/// The target amount of the frames rendered per second (actual game FPS).
/// </summary>
API_FIELD(Attributes="EditorOrder(3), DefaultValue(60.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Draw FPS\")")
API_FIELD(Attributes="EditorOrder(3), Limit(0, 1000), EditorDisplay(\"General\", \"Draw FPS\")")
float DrawFPS = 60.0f;
/// <summary>
/// The game time scale factor. Default is 1.
/// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(1.0f), Limit(0, 1000.0f, 0.1f), EditorDisplay(\"General\")")
API_FIELD(Attributes="EditorOrder(10), Limit(0, 1000.0f, 0.1f), EditorDisplay(\"General\")")
float TimeScale = 1.0f;
/// <summary>
/// The maximum allowed delta time (in seconds) for the game logic update step.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.1f), Limit(0.1f, 1000.0f, 0.01f), EditorDisplay(\"General\")")
API_FIELD(Attributes="EditorOrder(20), Limit(0.1f, 1000.0f, 0.01f), EditorDisplay(\"General\")")
float MaxUpdateDeltaTime = 0.1f;
public:

View File

@@ -4,11 +4,6 @@
#include "Engine/Core/Memory/Allocation.h"
template<typename>
class Function;
template<typename... Params>
class Delegate;
/// <summary>
/// The function object.
/// </summary>

View File

@@ -2,6 +2,8 @@
#pragma once
#include "Engine/Core/Compiler.h"
/// <summary>
/// Helper class used to delete another object.
/// </summary>

View File

@@ -37,21 +37,21 @@ public:
virtual ~ISerializable() = default;
/// <summary>
/// Serialize object to the output stream compared to the values of the other object instance (eg. default class object). If other object is null then serialize all properties.
/// Serializes object to the output stream compared to the values of the other object instance (eg. default class object). If other object is null then serialize all properties.
/// </summary>
/// <param name="stream">The output stream.</param>
/// <param name="otherObj">The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties.</param>
virtual void Serialize(SerializeStream& stream, const void* otherObj) = 0;
/// <summary>
/// Deserialize object from the input stream
/// Deserializes object from the input stream.
/// </summary>
/// <param name="stream">The input stream.</param>
/// <param name="modifier">The deserialization modifier object. Always valid.</param>
virtual void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) = 0;
/// <summary>
/// Deserialize object from the input stream child member. Won't deserialize it if member is missing.
/// Deserializes object from the input stream child member. Won't deserialize it if member is missing.
/// </summary>
/// <param name="stream">The input stream.</param>
/// <param name="memberName">The input stream member to lookup.</param>

View File

@@ -156,9 +156,9 @@ void Log::Logger::Dispose()
WriteFloor();
// Close
LogAfterInit = false;
if (LogAfterInit)
{
LogAfterInit = false;
LogFile->Close();
Delete(LogFile);
LogFile = nullptr;

View File

@@ -6,7 +6,7 @@
#include "../Types/String.h"
const BoundingBox BoundingBox::Empty(Vector3(MAX_float), Vector3(MIN_float));
const BoundingBox BoundingBox::Zero(Vector3(0.0f), Vector3(0.0f));
const BoundingBox BoundingBox::Zero(Vector3(0.0f));
String BoundingBox::ToString() const
{

View File

@@ -45,6 +45,16 @@ public:
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BoundingBox"/> struct.
/// </summary>
/// <param name="point">The location of the empty bounding box.</param>
BoundingBox(const Vector3& point)
: Minimum(point)
, Maximum(point)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BoundingBox"/> struct.
/// </summary>

View File

@@ -125,7 +125,7 @@ namespace FlaxEngine
public static class CollisionsHelper
{
/// <summary>
/// Determines the closest point between a point and a line.
/// Determines the closest point between a point and a line segment.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="p0">The line first point.</param>

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "Int2.h"
#include "Int3.h"
#include "Int4.h"
#include "Vector2.h"
#include "Vector3.h"
#include "Vector4.h"
#include "Engine/Core/Types/String.h"
static_assert(sizeof(Int2) == 8, "Invalid Int2 type size.");
const Int2 Int2::Zero(0);
const Int2 Int2::One(1);
const Int2 Int2::Minimum(MIN_int32);
const Int2 Int2::Maximum(MAX_int32);
Int2::Int2(const Int3& xyz)
: X(xyz.X)
, Y(xyz.Y)
{
}
Int2::Int2(const Int4& xyzw)
: X(xyzw.X)
, Y(xyzw.Y)
{
}
Int2::Int2(const Vector2& xy)
: X(static_cast<int32>(xy.X))
, Y(static_cast<int32>(xy.Y))
{
}
Int2::Int2(const Vector3& xyz)
: X(static_cast<int32>(xyz.X))
, Y(static_cast<int32>(xyz.Y))
{
}
Int2::Int2(const Vector4& xyzw)
: X(static_cast<int32>(xyzw.X))
, Y(static_cast<int32>(xyzw.Y))
{
}
String Int2::ToString() const
{
return String::Format(TEXT("{}"), *this);
}

View File

@@ -12,9 +12,8 @@ namespace FlaxEngine
/// Represents a two dimensional mathematical vector (signed integers).
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
[TypeConverter(typeof(TypeConverters.Int2Converter))]
public struct Int2 : IEquatable<Int2>, IFormattable
partial struct Int2 : IEquatable<Int2>, IFormattable
{
private static readonly string _formatString = "X:{0} Y:{1}";
@@ -53,16 +52,6 @@ namespace FlaxEngine
/// </summary>
public static readonly Int2 Maximum = new Int2(int.MaxValue);
/// <summary>
/// The X component of the vector.
/// </summary>
public int X;
/// <summary>
/// The Y component of the vector.
/// </summary>
public int Y;
/// <summary>
/// Initializes a new instance of the <see cref="Int2" /> struct.
/// </summary>

View File

@@ -6,26 +6,27 @@
#include "Engine/Core/Formatting.h"
#include "Engine/Core/Templates.h"
struct Vector2;
struct Vector3;
struct Vector4;
/// <summary>
/// Two-components vector (32 bit integer type).
/// </summary>
API_STRUCT(InBuild) struct FLAXENGINE_API Int2
API_STRUCT() struct FLAXENGINE_API Int2
{
DECLARE_SCRIPTING_TYPE_MINIMAL(Int2);
public:
union
{
struct
{
// X component
int32 X;
/// <summary>
/// The X component.
/// </summary>
API_FIELD() int32 X;
// Y component
int32 Y;
/// <summary>
/// The Y component.
/// </summary>
API_FIELD() int32 Y;
};
// Raw values
@@ -40,6 +41,12 @@ public:
// Vector with all components equal 1
static const Int2 One;
// A minimum Int2
static const Int2 Minimum;
// A maximum Int2
static const Int2 Maximum;
public:
/// <summary>
@@ -67,9 +74,25 @@ public:
}
// Init
// @param v Vector to use X and Y components
explicit Int2(const Vector2& v);
// @param xyz Int3 to use X and Y components
Int2(const Int3& xyz);
// Init
// @param xyzw Int4 to use X and Y components
Int2(const Int4& xyzw);
// Init
// @param xy Vector2 to use X and Y components
explicit Int2(const Vector2& xy);
// Init
// @param xyz Vector3 to use X and Y components
explicit Int2(const Vector3& xyz);
// Init
// @param xyzw Vector4 to use X and Y components
explicit Int2(const Vector4& xyzw);
public:
String ToString() const;
@@ -211,29 +234,29 @@ public:
public:
static void Add(const Int2& a, const Int2& b, Int2* result)
static void Add(const Int2& a, const Int2& b, Int2& result)
{
result->X = a.X + b.X;
result->Y = a.Y + b.Y;
result.X = a.X + b.X;
result.Y = a.Y + b.Y;
}
static Int2 Add(const Int2& a, const Int2& b)
{
Int2 result;
Add(a, b, &result);
Add(a, b, result);
return result;
}
static void Subtract(const Int2& a, const Int2& b, Int2* result)
static void Subtract(const Int2& a, const Int2& b, Int2& result)
{
result->X = a.X - b.X;
result->Y = a.Y - b.Y;
result.X = a.X - b.X;
result.Y = a.Y - b.Y;
}
static Int2 Subtract(const Int2& a, const Int2& b)
{
Int2 result;
Subtract(a, b, &result);
Subtract(a, b, result);
return result;
}
@@ -257,17 +280,112 @@ public:
return Int2(a.X / b, a.Y / b);
}
// Creates vector from minimum components of two vectors
/// <summary>
/// Gets a value indicting whether this vector is zero.
/// </summary>
/// <returns> True if the vector is zero, otherwise false.</returns>
bool IsZero() const
{
return X == 0 && Y == 0;
}
/// <summary>
/// Gets a value indicting whether any vector component is zero.
/// </summary>
/// <returns> True if a component is zero, otherwise false.</returns>
bool IsAnyZero() const
{
return X == 0 || Y == 0;
}
/// <summary>
/// Gets a value indicting whether this vector is one.
/// </summary>
/// <returns> True if the vector is one, otherwise false.</returns>
bool IsOne() const
{
return X == 1 && Y == 1;
}
/// <summary>
/// Calculates a vector with values being opposite to values of that vector
/// </summary>
/// <returns>Negative vector</returns>
Int2 GetNegative() const
{
return Int2(-X, -Y);
}
/// <summary>
/// Returns average arithmetic of all the components
/// </summary>
/// <returns>Average arithmetic of all the components</returns>
float AverageArithmetic() const
{
return (X + Y) * 0.5f;
}
/// <summary>
/// Gets sum of all vector components values
/// </summary>
/// <returns>Sum of X, Y, Z and W</returns>
int32 SumValues() const
{
return X + Y;
}
/// <summary>
/// Returns minimum value of all the components
/// </summary>
/// <returns>Minimum value</returns>
int32 MinValue() const
{
return Math::Min(X, Y);
}
/// <summary>
/// Returns maximum value of all the components
/// </summary>
/// <returns>Maximum value</returns>
int32 MaxValue() const
{
return Math::Max(X, Y);
}
// Returns a vector containing the smallest components of the specified vectors
// @param a The first source vector
// @param b The second source vector
static Int2 Min(const Int2& a, const Int2& b)
{
return Int2(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y);
}
// Creates vector from maximum components of two vectors
// Returns a vector containing the largest components of the specified vectors
// @param a The first source vector
// @param b The second source vector
static Int2 Max(const Int2& a, const Int2& b)
{
return Int2(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y);
}
// Returns a vector containing the smallest components of the specified vectors
// @param a The first source vector
// @param b The second source vector
// @param result When the method completes, contains an new vector composed of the smallest components of the source vectors
static void Min(const Int2& a, const Int2& b, Int2& result)
{
result = Int2(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y);
}
// Returns a vector containing the largest components of the specified vectors
// @param a The first source vector
// @param b The second source vector
// @param result When the method completes, contains an new vector composed of the largest components of the source vectors
static void Max(const Int2& a, const Int2& b, Int2& result)
{
result = Int2(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y);
}
};
template<>

View File

@@ -0,0 +1,56 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "Int2.h"
#include "Int3.h"
#include "Int4.h"
#include "Vector2.h"
#include "Vector3.h"
#include "Vector4.h"
#include "Engine/Core/Types/String.h"
static_assert(sizeof(Int3) == 12, "Invalid Int3 type size.");
const Int3 Int3::Zero(0);
const Int3 Int3::One(1);
const Int3 Int3::Minimum(MIN_int32);
const Int3 Int3::Maximum(MAX_int32);
Int3::Int3(const Int2& xy, int32 z)
: X(xy.X)
, Y(xy.Y)
, Z(z)
{
}
Int3::Int3(const Int4& xyzw)
: X(xyzw.X)
, Y(xyzw.Y)
, Z(xyzw.Z)
{
}
Int3::Int3(const Vector2& xy, int32 z)
: X(static_cast<int32>(xy.X))
, Y(static_cast<int32>(xy.Y))
, Z(z)
{
}
Int3::Int3(const Vector3& xyz)
: X(static_cast<int32>(xyz.X))
, Y(static_cast<int32>(xyz.Y))
, Z(static_cast<int32>(xyz.Z))
{
}
Int3::Int3(const Vector4& xyzw)
: X(static_cast<int32>(xyzw.X))
, Y(static_cast<int32>(xyzw.Y))
, Z(static_cast<int32>(xyzw.Z))
{
}
String Int3::ToString() const
{
return String::Format(TEXT("{}"), *this);
}

View File

@@ -12,9 +12,8 @@ namespace FlaxEngine
/// Represents a three dimensional mathematical vector (signed integers).
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
[TypeConverter(typeof(TypeConverters.Int3Converter))]
public struct Int3 : IEquatable<Int3>, IFormattable
partial struct Int3 : IEquatable<Int3>, IFormattable
{
private static readonly string _formatString = "X:{0} Y:{1} Z:{2}";
@@ -58,21 +57,6 @@ namespace FlaxEngine
/// </summary>
public static readonly Int3 Maximum = new Int3(int.MaxValue);
/// <summary>
/// The X component of the vector.
/// </summary>
public int X;
/// <summary>
/// The Y component of the vector.
/// </summary>
public int Y;
/// <summary>
/// The Z component of the vector.
/// </summary>
public int Z;
/// <summary>
/// Initializes a new instance of the <see cref="Int3" /> struct.
/// </summary>

View File

@@ -6,29 +6,33 @@
#include "Engine/Core/Formatting.h"
#include "Engine/Core/Templates.h"
struct Vector2;
struct Vector3;
struct Vector4;
/// <summary>
/// Three-components vector (32 bit integer type).
/// </summary>
API_STRUCT(InBuild) struct FLAXENGINE_API Int3
API_STRUCT() struct FLAXENGINE_API Int3
{
DECLARE_SCRIPTING_TYPE_MINIMAL(Int3);
public:
union
{
struct
{
// X component
int32 X;
/// <summary>
/// The X component.
/// </summary>
API_FIELD() int32 X;
// Y component
int32 Y;
/// <summary>
/// The Y component.
/// </summary>
API_FIELD() int32 Y;
// Y component
int32 Z;
/// <summary>
/// The Z component.
/// </summary>
API_FIELD() int32 Z;
};
// Raw values
@@ -43,6 +47,12 @@ public:
// Vector with all components equal 1
static const Int3 One;
// A minimum Int3
static const Int3 Minimum;
// A maximum Int3
static const Int3 Maximum;
public:
/// <summary>
@@ -73,19 +83,293 @@ public:
}
// Init
// @param v Vector to use X, Y and Z components
explicit Int3(const Vector3& v);
// @param v Int2 to use X and Y components
// @param z Z component value
Int3(const Int2& xy, int32 z);
// Init
// @param v Int4 to use X and Y components
Int3(const Int4& xyzw);
// Init
// @param v Vector2 to use X and Y components
// @param z Z component value
explicit Int3(const Vector2& xy, int32 z);
// Init
// @param v Vector3 to use X, Y and Z components
explicit Int3(const Vector3& xyz);
// Init
// @param v Vector4 to use X and Y components
explicit Int3(const Vector4& xyzw);
public:
String ToString() const;
public:
// Arithmetic operators with Int2
Int3 operator+(const Int3& b) const
{
return Add(*this, b);
}
Int3 operator-(const Int3& b) const
{
return Subtract(*this, b);
}
Int3 operator*(const Int3& b) const
{
return Multiply(*this, b);
}
Int3 operator/(const Int3& b) const
{
return Divide(*this, b);
}
Int3 operator-() const
{
return Int3(-X, -Y, -Z);
}
// op= operators with Int2
Int3& operator+=(const Int3& b)
{
*this = Add(*this, b);
return *this;
}
Int3& operator-=(const Int3& b)
{
*this = Subtract(*this, b);
return *this;
}
Int3& operator*=(const Int3& b)
{
*this = Multiply(*this, b);
return *this;
}
Int3& operator/=(const Int3& b)
{
*this = Divide(*this, b);
return *this;
}
// Arithmetic operators with int32
Int3 operator+(int32 b) const
{
return Add(*this, b);
}
Int3 operator-(int32 b) const
{
return Subtract(*this, b);
}
Int3 operator*(int32 b) const
{
return Multiply(*this, b);
}
Int3 operator/(int32 b) const
{
return Divide(*this, b);
}
// op= operators with int32
Int3& operator+=(int32 b)
{
*this = Add(*this, b);
return *this;
}
Int3& operator-=(int32 b)
{
*this = Subtract(*this, b);
return *this;
}
Int3& operator*=(int32 b)
{
*this = Multiply(*this, b);
return *this;
}
Int3& operator/=(int32 b)
{
*this = Divide(*this, b);
return *this;
}
// Comparison operators
bool operator==(const Int3& b) const
{
return X == b.X && Y == b.Y;
}
bool operator!=(const Int3& b) const
{
return X != b.X || Y != b.Y;
}
bool operator>(const Int3& b) const
{
return X > b.X && Y > b.Y;
}
bool operator>=(const Int3& b) const
{
return X >= b.X && Y >= b.Y;
}
bool operator<(const Int3& b) const
{
return X < b.X && Y < b.Y;
}
bool operator<=(const Int3& b) const
{
return X <= b.X && Y <= b.Y;
}
public:
static void Add(const Int3& a, const Int3& b, Int3& result)
{
result.X = a.X + b.X;
result.Y = a.Y + b.Y;
result.Z = a.Z + b.Z;
}
static Int3 Add(const Int3& a, const Int3& b)
{
Int3 result;
Add(a, b, result);
return result;
}
static void Subtract(const Int3& a, const Int3& b, Int3& result)
{
result.X = a.X - b.X;
result.Y = a.Y - b.Y;
result.Z = a.Z - b.Z;
}
static Int3 Subtract(const Int3& a, const Int3& b)
{
Int3 result;
Subtract(a, b, result);
return result;
}
static Int3 Multiply(const Int3& a, const Int3& b)
{
return Int3(a.X * b.X, a.Y * b.Y, a.Z * b.Z);
}
static Int3 Multiply(const Int3& a, int32 b)
{
return Int3(a.X * b, a.Y * b, a.Z * b);
}
static Int3 Divide(const Int3& a, const Int3& b)
{
return Int3(a.X / b.X, a.Y / b.Y, a.Z / b.Z);
}
static Int3 Divide(const Int3& a, int32 b)
{
return Int3(a.X / b, a.Y / b, a.Z / b);
}
public:
/// <summary>
/// Gets a value indicting whether this vector is zero.
/// </summary>
/// <returns> True if the vector is zero, otherwise false.</returns>
bool IsZero() const
{
return X == 0 && Y == 0 && Z == 0;
}
/// <summary>
/// Gets a value indicting whether any vector component is zero.
/// </summary>
/// <returns> True if a component is zero, otherwise false.</returns>
bool IsAnyZero() const
{
return X == 0 || Y == 0 || Z == 0;
}
/// <summary>
/// Gets a value indicting whether this vector is one.
/// </summary>
/// <returns> True if the vector is one, otherwise false.</returns>
bool IsOne() const
{
return X == 1 && Y == 1 && Z == 1;
}
/// <summary>
/// Calculates a vector with values being opposite to values of that vector
/// </summary>
/// <returns>Negative vector</returns>
Int3 GetNegative() const
{
return Int3(-X, -Y, -Z);
}
/// <summary>
/// Returns average arithmetic of all the components
/// </summary>
/// <returns>Average arithmetic of all the components</returns>
float AverageArithmetic() const
{
return (X + Y + Z) / 3.0f;
}
/// <summary>
/// Gets sum of all vector components values
/// </summary>
/// <returns>Sum of X, Y, Z and W</returns>
int32 SumValues() const
{
return X + Y + Z;
}
/// <summary>
/// Returns minimum value of all the components
/// </summary>
/// <returns>Minimum value</returns>
int32 MinValue() const
{
return Math::Min(X, Y, Z);
}
/// <summary>
/// Returns maximum value of all the components
/// </summary>
/// <returns>Maximum value</returns>
int32 MaxValue() const
{
return Math::Max(X, Y, Z);
}
// Returns a vector containing the largest components of the specified vectors
// @param a The first source vector
// @param b The second source vector
// @param result When the method completes, contains an new vector composed of the largest components of the source vectors
static Int3 Max(const Int3& a, const Int3& b)
{
return Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z);
@@ -94,7 +378,6 @@ public:
// Returns a vector containing the smallest components of the specified vectors
// @param a The first source vector
// @param b The second source vector
// @param result When the method completes, contains an new vector composed of the smallest components of the source vectors
static Int3 Min(const Int3& a, const Int3& b)
{
return Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z);
@@ -104,18 +387,18 @@ public:
// @param a The first source vector
// @param b The second source vector
// @param result When the method completes, contains an new vector composed of the largest components of the source vectors
static void Max(const Int3& a, const Int3& b, Int3* result)
static void Max(const Int3& a, const Int3& b, Int3& result)
{
*result = Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z);
result = Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z);
}
// Returns a vector containing the smallest components of the specified vectors
// @param a The first source vector
// @param b The second source vector
// @param result When the method completes, contains an new vector composed of the smallest components of the source vectors
static void Min(const Int3& a, const Int3& b, Int3* result)
static void Min(const Int3& a, const Int3& b, Int3 result)
{
*result = Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z);
result = Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z);
}
};

View File

@@ -1,42 +1,51 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "VectorInt.h"
#include "Int2.h"
#include "Int3.h"
#include "Int4.h"
#include "Vector2.h"
#include "Vector3.h"
#include "Vector4.h"
#include "Engine/Core/Types/String.h"
const Int2 Int2::Zero(0);
const Int2 Int2::One(1);
Int2::Int2(const Vector2& v)
: X(static_cast<int32>(v.X))
, Y(static_cast<int32>(v.Y))
{
}
String Int2::ToString() const
{
return String::Format(TEXT("{}"), *this);
}
const Int3 Int3::Zero(0);
const Int3 Int3::One(1);
Int3::Int3(const Vector3& v)
: X(static_cast<int32>(v.X))
, Y(static_cast<int32>(v.Y))
, Z(static_cast<int32>(v.Z))
{
}
String Int3::ToString() const
{
return String::Format(TEXT("{}"), *this);
}
static_assert(sizeof(Int4) == 16, "Invalid Int4 type size.");
const Int4 Int4::Zero(0);
const Int4 Int4::One(1);
const Int4 Int4::Minimum(MIN_int32);
const Int4 Int4::Maximum(MAX_int32);
Int4::Int4(const Int2& xy, int32 z, int32 w)
: X(xy.X)
, Y(xy.Y)
, Z(z)
, W(w)
{
}
Int4::Int4(const Int3& xyz, int32 w)
: X(xyz.X)
, Y(xyz.Y)
, Z(xyz.Z)
, W(w)
{
}
Int4::Int4(const Vector2& v, int32 z, int32 w)
: X(static_cast<int32>(v.X))
, Y(static_cast<int32>(v.Y))
, Z(z)
, W(w)
{
}
Int4::Int4(const Vector3& v, int32 w)
: X(static_cast<int32>(v.X))
, Y(static_cast<int32>(v.Y))
, Z(static_cast<int32>(v.Z))
, W(w)
{
}
Int4::Int4(const Vector4& v)
: X(static_cast<int32>(v.X))

View File

@@ -12,9 +12,8 @@ namespace FlaxEngine
/// Represents a four dimensional mathematical vector (signed integers).
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
[TypeConverter(typeof(TypeConverters.Int4Converter))]
public struct Int4 : IEquatable<Int4>, IFormattable
partial struct Int4 : IEquatable<Int4>, IFormattable
{
private static readonly string _formatString = "X:{0} Y:{1} Z:{2} W:{3}";
@@ -63,26 +62,6 @@ namespace FlaxEngine
/// </summary>
public static readonly Int4 Maximum = new Int4(int.MaxValue);
/// <summary>
/// The X component of the vector.
/// </summary>
public int X;
/// <summary>
/// The Y component of the vector.
/// </summary>
public int Y;
/// <summary>
/// The Z component of the vector.
/// </summary>
public int Z;
/// <summary>
/// The W component of the vector.
/// </summary>
public int W;
/// <summary>
/// Initializes a new instance of the <see cref="Int4" /> struct.
/// </summary>

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