Merge remote-tracking branch 'origin/master' into 1.8

# Conflicts:
#	Source/Editor/Utilities/EditorUtilities.cpp
#	Source/Editor/Utilities/EditorUtilities.h
This commit is contained in:
Wojtek Figat
2024-02-19 22:26:16 +01:00
219 changed files with 4189 additions and 2372 deletions

View File

@@ -146,7 +146,7 @@ namespace FlaxEngine
public string Path;
/// <summary>
/// Initializes a new instance of the <see cref="BehaviorKnowledgeSelectorAny"/> structure.
/// Initializes a new instance of the <see cref="BehaviorKnowledgeSelector{T}"/> structure.
/// </summary>
/// <param name="path">The selector path.</param>
public BehaviorKnowledgeSelector(string path)
@@ -155,7 +155,7 @@ namespace FlaxEngine
}
/// <summary>
/// Initializes a new instance of the <see cref="BehaviorKnowledgeSelectorAny"/> structure.
/// Initializes a new instance of the <see cref="BehaviorKnowledgeSelector{T}"/> structure.
/// </summary>
/// <param name="other">The other selector.</param>
public BehaviorKnowledgeSelector(BehaviorKnowledgeSelectorAny other)

View File

@@ -91,6 +91,13 @@ API_STRUCT(InBuild, Template, MarshalAs=StringAnsi) struct FLAXENGINE_API Behavi
return false;
}
BehaviorKnowledgeSelector() = default;
BehaviorKnowledgeSelector(const StringAnsi& other)
{
Path = other;
}
BehaviorKnowledgeSelector& operator=(const StringAnsiView& other) noexcept
{
Path = other;

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "AnimationData.h"
void NodeAnimationData::Evaluate(float time, Transform* result, bool loop) const
{
if (Position.GetKeyframes().HasItems())
#if USE_LARGE_WORLDS
{
Float3 position;
Position.Evaluate(position, time, loop);
result->Translation = position;
}
#else
Position.Evaluate(result->Translation, time, loop);
#endif
if (Rotation.GetKeyframes().HasItems())
Rotation.Evaluate(result->Orientation, time, loop);
if (Scale.GetKeyframes().HasItems())
Scale.Evaluate(result->Scale, time, loop);
}
void NodeAnimationData::EvaluateAll(float time, Transform* result, bool loop) const
{
Float3 position;
Position.Evaluate(position, time, loop);
result->Translation = position;
Rotation.Evaluate(result->Orientation, time, loop);
Scale.Evaluate(result->Scale, time, loop);
}
int32 NodeAnimationData::GetKeyframesCount() const
{
return Position.GetKeyframes().Count() + Rotation.GetKeyframes().Count() + Scale.GetKeyframes().Count();
}
uint64 NodeAnimationData::GetMemoryUsage() const
{
return NodeName.Length() * sizeof(Char) + Position.GetMemoryUsage() + Rotation.GetMemoryUsage() + Scale.GetMemoryUsage();
}
uint64 AnimationData::GetMemoryUsage() const
{
uint64 result = (Name.Length() + RootNodeName.Length()) * sizeof(Char) + Channels.Capacity() * sizeof(NodeAnimationData);
for (const auto& e : Channels)
result += e.GetMemoryUsage();
return result;
}
int32 AnimationData::GetKeyframesCount() const
{
int32 result = 0;
for (int32 i = 0; i < Channels.Count(); i++)
result += Channels[i].GetKeyframesCount();
return result;
}
NodeAnimationData* AnimationData::GetChannel(const StringView& name)
{
for (auto& e : Channels)
if (e.NodeName == name)
return &e;
return nullptr;
}
void AnimationData::Swap(AnimationData& other)
{
::Swap(Duration, other.Duration);
::Swap(FramesPerSecond, other.FramesPerSecond);
::Swap(RootMotionFlags, other.RootMotionFlags);
::Swap(Name, other.Name);
::Swap(RootNodeName, other.RootNodeName);
Channels.Swap(other.Channels);
}
void AnimationData::Dispose()
{
Name.Clear();
Duration = 0.0;
FramesPerSecond = 0.0;
RootNodeName.Clear();
RootMotionFlags = AnimationRootMotionFlags::None;
Channels.Resize(0);
}

View File

@@ -3,8 +3,8 @@
#pragma once
#include "Engine/Core/Types/String.h"
#include "Engine/Animations/Curve.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Animations/Curve.h"
/// <summary>
/// Single node animation data container.
@@ -50,19 +50,7 @@ public:
/// <param name="time">The time to evaluate the curves at.</param>
/// <param name="result">The interpolated value from the curve at provided time.</param>
/// <param name="loop">If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped.</param>
void Evaluate(float time, Transform* result, bool loop = true) const
{
if (Position.GetKeyframes().HasItems())
{
Float3 position;
Position.Evaluate(position, time, loop);
result->Translation = position;
}
if (Rotation.GetKeyframes().HasItems())
Rotation.Evaluate(result->Orientation, time, loop);
if (Scale.GetKeyframes().HasItems())
Scale.Evaluate(result->Scale, time, loop);
}
void Evaluate(float time, Transform* result, bool loop = true) const;
/// <summary>
/// Evaluates the animation transformation at the specified time.
@@ -70,29 +58,37 @@ public:
/// <param name="time">The time to evaluate the curves at.</param>
/// <param name="result">The interpolated value from the curve at provided time.</param>
/// <param name="loop">If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped.</param>
void EvaluateAll(float time, Transform* result, bool loop = true) const
{
Float3 position;
Position.Evaluate(position, time, loop);
result->Translation = position;
Rotation.Evaluate(result->Orientation, time, loop);
Scale.Evaluate(result->Scale, time, loop);
}
void EvaluateAll(float time, Transform* result, bool loop = true) const;
/// <summary>
/// Gets the total amount of keyframes in the animation curves.
/// </summary>
int32 GetKeyframesCount() const
{
return Position.GetKeyframes().Count() + Rotation.GetKeyframes().Count() + Scale.GetKeyframes().Count();
}
int32 GetKeyframesCount() const;
uint64 GetMemoryUsage() const
{
return NodeName.Length() * sizeof(Char) + Position.GetMemoryUsage() + Rotation.GetMemoryUsage() + Scale.GetMemoryUsage();
}
uint64 GetMemoryUsage() const;
};
/// <summary>
/// Root Motion modes that can be applied by the animation. Used as flags for selective behavior.
/// </summary>
API_ENUM(Attributes="Flags") enum class AnimationRootMotionFlags : byte
{
// No root motion.
None = 0,
// Root node position along XZ plane. Applies horizontal movement. Good for stationary animations (eg. idle).
RootPositionXZ = 1 << 0,
// Root node position along Y axis (up). Applies vertical movement. Good for all 'grounded' animations unless jumping is handled from code.
RootPositionY = 1 << 1,
// Root node rotation. Applies orientation changes. Good for animations that have baked-in root rotation (eg. turn animations).
RootRotation = 1 << 2,
// Root node position.
RootPosition = RootPositionXZ | RootPositionY,
// Root node position and rotation.
RootTransform = RootPosition | RootRotation,
};
DECLARE_ENUM_OPERATORS(AnimationRootMotionFlags);
/// <summary>
/// Skeleton nodes animation data container. Includes metadata about animation sampling, duration and node animations curves.
/// </summary>
@@ -111,7 +107,7 @@ struct AnimationData
/// <summary>
/// Enables root motion extraction support from this animation.
/// </summary>
bool EnableRootMotion = false;
AnimationRootMotionFlags RootMotionFlags = AnimationRootMotionFlags::None;
/// <summary>
/// The animation name.
@@ -140,49 +136,23 @@ public:
return static_cast<float>(Duration / FramesPerSecond);
}
uint64 GetMemoryUsage() const
{
uint64 result = (Name.Length() + RootNodeName.Length()) * sizeof(Char) + Channels.Capacity() * sizeof(NodeAnimationData);
for (const auto& e : Channels)
result += e.GetMemoryUsage();
return result;
}
uint64 GetMemoryUsage() const;
/// <summary>
/// Gets the total amount of keyframes in the all animation channels.
/// </summary>
int32 GetKeyframesCount() const
{
int32 result = 0;
for (int32 i = 0; i < Channels.Count(); i++)
result += Channels[i].GetKeyframesCount();
return result;
}
int32 GetKeyframesCount() const;
NodeAnimationData* GetChannel(const StringView& name);
/// <summary>
/// Swaps the contents of object with the other object without copy operation. Performs fast internal data exchange.
/// </summary>
/// <param name="other">The other object.</param>
void Swap(AnimationData& other)
{
::Swap(Duration, other.Duration);
::Swap(FramesPerSecond, other.FramesPerSecond);
::Swap(EnableRootMotion, other.EnableRootMotion);
::Swap(Name, other.Name);
::Swap(RootNodeName, other.RootNodeName);
Channels.Swap(other.Channels);
}
void Swap(AnimationData& other);
/// <summary>
/// Releases data.
/// </summary>
void Dispose()
{
Name.Clear();
Duration = 0.0;
FramesPerSecond = 0.0;
RootNodeName.Clear();
EnableRootMotion = false;
Channels.Resize(0);
}
void Dispose();
};

View File

@@ -52,7 +52,7 @@ namespace
AnimationsService AnimationManagerInstance;
TaskGraphSystem* Animations::System = nullptr;
#if USE_EDITOR
Delegate<Asset*, ScriptingObject*, uint32, uint32> Animations::DebugFlow;
Delegate<Animations::DebugFlowInfo> Animations::DebugFlow;
#endif
AnimEvent::AnimEvent(const SpawnParams& params)
@@ -127,7 +127,7 @@ void AnimationsSystem::Execute(TaskGraph* graph)
#if USE_EDITOR
// If debug flow is registered, then warm it up (eg. static cached method inside DebugFlow_ManagedWrapper) so it doesn't crash on highly multi-threaded code
if (Animations::DebugFlow.IsBinded())
Animations::DebugFlow(nullptr, nullptr, 0, 0);
Animations::DebugFlow(Animations::DebugFlowInfo());
#endif
// Schedule work to update all animated models in async

View File

@@ -22,8 +22,25 @@ API_CLASS(Static) class FLAXENGINE_API Animations
API_FIELD(ReadOnly) static TaskGraphSystem* System;
#if USE_EDITOR
// Custom event that is called every time the Anim Graph signal flows over the graph (including the data connections). Can be used to read and visualize the animation blending logic. Args are: anim graph asset, animated object, node id, box id
API_EVENT() static Delegate<Asset*, ScriptingObject*, uint32, uint32> DebugFlow;
// Data wrapper for the debug flow information.
API_STRUCT(NoDefault) struct DebugFlowInfo
{
DECLARE_SCRIPTING_TYPE_MINIMAL(DebugFlowInfo);
// Anim Graph asset
API_FIELD() Asset* Asset = nullptr;
// Animated actor
API_FIELD() ScriptingObject* Instance = nullptr;
// Graph node id.
API_FIELD() uint32 NodeId = 0;
// Graph box id.
API_FIELD() uint32 BoxId = 0;
// Ids of graph nodes (call of hierarchy).
API_FIELD(Internal, NoArray) uint32 NodePath[8] = {};
};
// Custom event that is called every time the Anim Graph signal flows over the graph (including the data connections). Can be used to read and visualize the animation blending logic.
API_EVENT() static Delegate<DebugFlowInfo> DebugFlow;
#endif
/// <summary>

View File

@@ -730,7 +730,7 @@ public:
void TransformTime(float timeScale, float timeOffset)
{
for (int32 i = 0; i < _keyframes.Count(); i++)
_keyframes[i].Time = _keyframes[i].Time * timeScale + timeOffset;;
_keyframes[i].Time = _keyframes[i].Time * timeScale + timeOffset;
}
uint64 GetMemoryUsage() const

View File

@@ -157,7 +157,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n)
if (_rootNode->Values.Count() < 1)
{
_rootNode->Values.Resize(1);
_rootNode->Values[0] = (int32)RootMotionMode::NoExtraction;
_rootNode->Values[0] = (int32)RootMotionExtraction::NoExtraction;
}
break;
// Animation

View File

@@ -89,7 +89,7 @@ void AnimGraphExecutor::initRuntime()
void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value& value)
{
#if USE_CSHARP
auto& context = Context.Get();
auto& context = *Context.Get();
if (context.ValueCache.TryGet(boxBase, value))
return;
auto box = (AnimGraphBox*)boxBase;

View File

@@ -9,7 +9,7 @@
extern void RetargetSkeletonNode(const SkeletonData& sourceSkeleton, const SkeletonData& targetSkeleton, const SkinnedModel::SkeletonMapping& sourceMapping, Transform& node, int32 i);
ThreadLocal<AnimGraphContext> AnimGraphExecutor::Context;
ThreadLocal<AnimGraphContext*> AnimGraphExecutor::Context;
Transform AnimGraphImpulse::GetNodeModelTransformation(SkeletonData& skeleton, int32 nodeIndex) const
{
@@ -104,7 +104,7 @@ AnimGraphInstanceData::OutgoingEvent AnimGraphInstanceData::ActiveEvent::End(Ani
AnimGraphImpulse* AnimGraphNode::GetNodes(AnimGraphExecutor* executor)
{
auto& context = AnimGraphExecutor::Context.Get();
auto& context = *AnimGraphExecutor::Context.Get();
const int32 count = executor->_skeletonNodesCount;
if (context.PoseCacheSize == context.PoseCache.Count())
context.PoseCache.AddOne();
@@ -204,19 +204,24 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
// Initialize
auto& skeleton = _graph.BaseModel->Skeleton;
auto& context = Context.Get();
auto& contextPtr = Context.Get();
if (!contextPtr)
contextPtr = New<AnimGraphContext>();
auto& context = *contextPtr;
{
ANIM_GRAPH_PROFILE_EVENT("Init");
// Init data from base model
_skeletonNodesCount = skeleton.Nodes.Count();
_rootMotionMode = (RootMotionMode)(int32)_graph._rootNode->Values[0];
_rootMotionMode = (RootMotionExtraction)(int32)_graph._rootNode->Values[0];
// Prepare context data for the evaluation
context.GraphStack.Clear();
context.GraphStack.Push((Graph*)&_graph);
context.NodePath.Clear();
context.Data = &data;
context.DeltaTime = dt;
context.StackOverFlow = false;
context.CurrentFrameIndex = ++data.CurrentFrame;
context.CallStack.Clear();
context.Functions.Clear();
@@ -377,12 +382,12 @@ void AnimGraphExecutor::GetInputValue(Box* box, Value& result)
AnimGraphImpulse* AnimGraphExecutor::GetEmptyNodes()
{
return &Context.Get().EmptyNodes;
return &Context.Get()->EmptyNodes;
}
void AnimGraphExecutor::InitNodes(AnimGraphImpulse* nodes) const
{
const auto& emptyNodes = Context.Get().EmptyNodes;
const auto& emptyNodes = Context.Get()->EmptyNodes;
Platform::MemoryCopy(nodes->Nodes.Get(), emptyNodes.Nodes.Get(), sizeof(Transform) * _skeletonNodesCount);
nodes->RootMotion = emptyNodes.RootMotion;
nodes->Position = emptyNodes.Position;
@@ -404,12 +409,15 @@ void AnimGraphExecutor::ResetBuckets(AnimGraphContext& context, AnimGraphBase* g
VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box)
{
auto& context = Context.Get();
auto& context = *Context.Get();
// Check if graph is looped or is too deep
if (context.StackOverFlow)
return Value::Zero;
if (context.CallStack.Count() >= ANIM_GRAPH_MAX_CALL_STACK)
{
OnError(caller, box, TEXT("Graph is looped or too deep!"));
context.StackOverFlow = true;
return Value::Zero;
}
#if !BUILD_RELEASE
@@ -424,7 +432,15 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box)
context.CallStack.Add(caller);
#if USE_EDITOR
Animations::DebugFlow(_graph._owner, context.Data->Object, box->GetParent<Node>()->ID, box->ID);
Animations::DebugFlowInfo flowInfo;
flowInfo.Asset = _graph._owner;
flowInfo.Instance = context.Data->Object;
flowInfo.NodeId = box->GetParent<Node>()->ID;
flowInfo.BoxId = box->ID;
const auto* nodePath = context.NodePath.Get();
for (int32 i = 0; i < context.NodePath.Count(); i++)
flowInfo.NodePath[i] = nodePath[i];
Animations::DebugFlow(flowInfo);
#endif
// Call per group custom processing event
@@ -441,6 +457,6 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box)
VisjectExecutor::Graph* AnimGraphExecutor::GetCurrentGraph() const
{
auto& context = Context.Get();
auto& context = *Context.Get();
return context.GraphStack.Peek();
}

View File

@@ -92,9 +92,9 @@ enum class BoneTransformMode
};
/// <summary>
/// The animated model root motion mode.
/// The animated model root motion extraction modes.
/// </summary>
enum class RootMotionMode
enum class RootMotionExtraction
{
/// <summary>
/// Don't extract nor apply the root motion.
@@ -205,7 +205,7 @@ struct FLAXENGINE_API AnimGraphSlot
/// <summary>
/// The animation graph state container for a single node playback trace (eg. animation sample info or state transition). Can be used by Anim Graph debugging or custom scripting.
/// </summary>
API_STRUCT() struct FLAXENGINE_API AnimGraphTraceEvent
API_STRUCT(NoDefault) struct FLAXENGINE_API AnimGraphTraceEvent
{
DECLARE_SCRIPTING_TYPE_MINIMAL(AnimGraphTraceEvent);
@@ -215,6 +215,8 @@ API_STRUCT() struct FLAXENGINE_API AnimGraphTraceEvent
API_FIELD() float Value = 0;
// Identifier of the node in the graph.
API_FIELD() uint32 NodeId = 0;
// Ids of graph nodes (call of hierarchy).
API_FIELD(Internal, NoArray) uint32 NodePath[8] = {};
};
/// <summary>
@@ -794,12 +796,16 @@ struct AnimGraphContext
AnimGraphInstanceData* Data;
AnimGraphImpulse EmptyNodes;
AnimGraphTransitionData TransitionData;
bool StackOverFlow;
Array<VisjectExecutor::Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> CallStack;
Array<VisjectExecutor::Graph*, FixedAllocation<32>> GraphStack;
Array<uint32, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK> > NodePath;
Dictionary<VisjectExecutor::Node*, VisjectExecutor::Graph*> Functions;
ChunkedArray<AnimGraphImpulse, 256> PoseCache;
int32 PoseCacheSize;
Dictionary<VisjectExecutor::Box*, Variant> ValueCache;
AnimGraphTraceEvent& AddTraceEvent(const AnimGraphNode* node);
};
/// <summary>
@@ -810,11 +816,11 @@ class AnimGraphExecutor : public VisjectExecutor
friend AnimGraphNode;
private:
AnimGraph& _graph;
RootMotionMode _rootMotionMode = RootMotionMode::NoExtraction;
RootMotionExtraction _rootMotionMode = RootMotionExtraction::NoExtraction;
int32 _skeletonNodesCount = 0;
// Per-thread context to allow async execution
static ThreadLocal<AnimGraphContext> Context;
static ThreadLocal<AnimGraphContext*> Context;
public:
/// <summary>
@@ -891,7 +897,7 @@ private:
Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, float speedA, float speedB, float alpha);
Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, Animation* animC, float speedA, float speedB, float speedC, float alphaA, float alphaB, float alphaC);
Variant Blend(AnimGraphNode* node, const Value& poseA, const Value& poseB, float alpha, AlphaBlendMode alphaMode);
Variant SampleState(AnimGraphNode* state);
Variant SampleState(AnimGraphContext& context, const AnimGraphNode* state);
void InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition = nullptr);
AnimGraphStateTransition* UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState = nullptr);
void UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData);

View File

@@ -21,13 +21,13 @@ namespace
base += additive;
}
FORCE_INLINE void NormalizeRotations(AnimGraphImpulse* nodes, RootMotionMode rootMotionMode)
FORCE_INLINE void NormalizeRotations(AnimGraphImpulse* nodes, RootMotionExtraction rootMotionMode)
{
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
{
nodes->Nodes[i].Orientation.Normalize();
}
if (rootMotionMode != RootMotionMode::NoExtraction)
if (rootMotionMode != RootMotionExtraction::NoExtraction)
{
nodes->RootMotion.Orientation.Normalize();
}
@@ -52,6 +52,17 @@ void RetargetSkeletonNode(const SkeletonData& sourceSkeleton, const SkeletonData
node = value;
}
AnimGraphTraceEvent& AnimGraphContext::AddTraceEvent(const AnimGraphNode* node)
{
auto& trace = Data->TraceEvents.AddOne();
trace.Value = 0.0f;
trace.NodeId = node->ID;
const auto* nodePath = NodePath.Get();
for (int32 i = 0; i < NodePath.Count(); i++)
trace.NodePath[i] = nodePath[i];
return trace;
}
int32 AnimGraphExecutor::GetRootNodeIndex(Animation* anim)
{
// TODO: cache the root node index (use dictionary with Animation* -> int32 for fast lookups)
@@ -76,7 +87,7 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float
if (anim->Events.Count() == 0)
return;
ANIM_GRAPH_PROFILE_EVENT("Events");
auto& context = Context.Get();
auto& context = *Context.Get();
float eventTimeMin = animPrevPos;
float eventTimeMax = animPos;
if (loop && context.DeltaTime * speed < 0)
@@ -220,13 +231,12 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
const float animPrevPos = GetAnimSamplePos(length, anim, prevPos, speed);
// Add to trace
auto& context = Context.Get();
auto& context = *Context.Get();
if (context.Data->EnableTracing)
{
auto& trace = context.Data->TraceEvents.AddOne();
auto& trace = context.AddTraceEvent(node);
trace.Asset = anim;
trace.Value = animPos;
trace.NodeId = node->ID;
}
// Evaluate nested animations
@@ -313,16 +323,21 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
}
// Handle root motion
if (_rootMotionMode != RootMotionMode::NoExtraction && anim->Data.EnableRootMotion)
if (_rootMotionMode != RootMotionExtraction::NoExtraction && anim->Data.RootMotionFlags != AnimationRootMotionFlags::None)
{
// Calculate the root motion node transformation
const bool motionPositionXZ = EnumHasAnyFlags(anim->Data.RootMotionFlags, AnimationRootMotionFlags::RootPositionXZ);
const bool motionPositionY = EnumHasAnyFlags(anim->Data.RootMotionFlags, AnimationRootMotionFlags::RootPositionY);
const bool motionRotation = EnumHasAnyFlags(anim->Data.RootMotionFlags, AnimationRootMotionFlags::RootRotation);
const Vector3 motionPositionMask(motionPositionXZ ? 1.0f : 0.0f, motionPositionY ? 1.0f : 0.0f, motionPositionXZ ? 1.0f : 0.0f);
const bool motionPosition = motionPositionXZ | motionPositionY;
const int32 rootNodeIndex = GetRootNodeIndex(anim);
const Transform& refPose = emptyNodes->Nodes[rootNodeIndex];
Transform& rootNode = nodes->Nodes[rootNodeIndex];
Transform& dstNode = nodes->RootMotion;
Transform srcNode = Transform::Identity;
const int32 nodeToChannel = mapping.NodesMapping[rootNodeIndex];
if (_rootMotionMode == RootMotionMode::Enable && nodeToChannel != -1)
if (_rootMotionMode == RootMotionExtraction::Enable && nodeToChannel != -1)
{
// Get the root bone transformation
Transform rootBefore = refPose;
@@ -346,16 +361,20 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
// Complex motion calculation to preserve the looped movement
// (end - before + now - begin)
// It sums the motion since the last update to anim end and since the start to now
srcNode.Translation = rootEnd.Translation - rootBefore.Translation + rootNode.Translation - rootBegin.Translation;
srcNode.Orientation = rootEnd.Orientation * rootBefore.Orientation.Conjugated() * (rootNode.Orientation * rootBegin.Orientation.Conjugated());
if (motionPosition)
srcNode.Translation = (rootEnd.Translation - rootBefore.Translation + rootNode.Translation - rootBegin.Translation) * motionPositionMask;
if (motionRotation)
srcNode.Orientation = rootEnd.Orientation * rootBefore.Orientation.Conjugated() * (rootNode.Orientation * rootBegin.Orientation.Conjugated());
//srcNode.Orientation = Quaternion::Identity;
}
else
{
// Simple motion delta
// (now - before)
srcNode.Translation = rootNode.Translation - rootBefore.Translation;
srcNode.Orientation = rootBefore.Orientation.Conjugated() * rootNode.Orientation;
if (motionPosition)
srcNode.Translation = (rootNode.Translation - rootBefore.Translation) * motionPositionMask;
if (motionRotation)
srcNode.Orientation = rootBefore.Orientation.Conjugated() * rootNode.Orientation;
}
// Convert root motion from local-space to the actor-space (eg. if root node is not actually a root and its parents have rotation/scale)
@@ -369,28 +388,40 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
}
}
// Remove root node motion after extraction
rootNode = refPose;
// Remove root node motion after extraction (only extracted components)
if (motionPosition)
rootNode.Translation = refPose.Translation * motionPositionMask + rootNode.Translation * (Vector3::One - motionPositionMask);
if (motionRotation)
rootNode.Orientation = refPose.Orientation;
// Blend root motion
if (mode == ProcessAnimationMode::BlendAdditive)
{
dstNode.Translation += srcNode.Translation * weight;
BlendAdditiveWeightedRotation(dstNode.Orientation, srcNode.Orientation, weight);
if (motionPosition)
dstNode.Translation += srcNode.Translation * weight * motionPositionMask;
if (motionRotation)
BlendAdditiveWeightedRotation(dstNode.Orientation, srcNode.Orientation, weight);
}
else if (mode == ProcessAnimationMode::Add)
{
dstNode.Translation += srcNode.Translation * weight;
dstNode.Orientation += srcNode.Orientation * weight;
if (motionPosition)
dstNode.Translation += srcNode.Translation * weight * motionPositionMask;
if (motionRotation)
dstNode.Orientation += srcNode.Orientation * weight;
}
else if (weighted)
{
dstNode.Translation = srcNode.Translation * weight;
dstNode.Orientation = srcNode.Orientation * weight;
if (motionPosition)
dstNode.Translation = srcNode.Translation * weight * motionPositionMask;
if (motionRotation)
dstNode.Orientation = srcNode.Orientation * weight;
}
else
{
dstNode = srcNode;
if (motionPosition)
dstNode.Translation = srcNode.Translation * motionPositionMask;
if (motionRotation)
dstNode.Orientation = srcNode.Orientation;
}
}
@@ -494,19 +525,30 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const
return nodes;
}
Variant AnimGraphExecutor::SampleState(AnimGraphNode* state)
Variant AnimGraphExecutor::SampleState(AnimGraphContext& context, const AnimGraphNode* state)
{
auto& data = state->Data.State;
if (data.Graph == nullptr || data.Graph->GetRootNode() == nullptr)
return Value::Null;
// Add to trace
if (context.Data->EnableTracing)
{
auto& trace = context.AddTraceEvent(state);
}
ANIM_GRAPH_PROFILE_EVENT("Evaluate State");
context.NodePath.Add(state->ID);
auto rootNode = data.Graph->GetRootNode();
return eatBox((Node*)rootNode, &rootNode->Boxes[0]);
auto result = eatBox((Node*)rootNode, &rootNode->Boxes[0]);
context.NodePath.Pop();
return result;
}
void AnimGraphExecutor::InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition)
{
// Reset transiton
// Reset transition
stateMachineBucket.ActiveTransition = transition;
stateMachineBucket.TransitionPosition = 0.0f;
@@ -537,7 +579,7 @@ AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphCon
}
// Evaluate source state transition data (position, length, etc.)
const Value sourceStatePtr = SampleState(state);
const Value sourceStatePtr = SampleState(context, state);
auto& transitionData = context.TransitionData; // Note: this could support nested transitions but who uses state machine inside transition rule?
if (ANIM_GRAPH_IS_VALID_PTR(sourceStatePtr))
{
@@ -634,7 +676,7 @@ void ComputeMultiBlendLength(float& length, AnimGraphNode* node)
void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value)
{
auto& context = Context.Get();
auto& context = *Context.Get();
switch (node->TypeID)
{
// Get
@@ -745,7 +787,7 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu
void AnimGraphExecutor::ProcessGroupTools(Box* box, Node* nodeBase, Value& value)
{
auto& context = Context.Get();
auto& context = *Context.Get();
auto node = (AnimGraphNode*)nodeBase;
switch (node->TypeID)
{
@@ -769,7 +811,7 @@ void AnimGraphExecutor::ProcessGroupTools(Box* box, Node* nodeBase, Value& value
void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Value& value)
{
auto& context = Context.Get();
auto& context = *Context.Get();
if (context.ValueCache.TryGet(boxBase, value))
return;
auto box = (AnimGraphBox*)boxBase;
@@ -1660,6 +1702,8 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
bucket.CurrentState = bucket.ActiveTransition->Destination; \
InitStateTransition(context, bucket)
context.NodePath.Push(node->ID);
// Update the active transition
if (bucket.ActiveTransition)
{
@@ -1767,11 +1811,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
if (bucket.BaseTransitionState)
{
// Sample the other state (eg. when blending from interrupted state to the another state from the old destination)
value = SampleState(bucket.BaseTransitionState);
value = SampleState(context, bucket.BaseTransitionState);
if (bucket.BaseTransition)
{
// Evaluate the base pose from the time when transition was interrupted
const auto destinationState = SampleState(bucket.BaseTransition->Destination);
const auto destinationState = SampleState(context, bucket.BaseTransition->Destination);
const float alpha = bucket.BaseTransitionPosition / bucket.BaseTransition->BlendDuration;
value = Blend(node, value, destinationState, alpha, bucket.BaseTransition->BlendMode);
}
@@ -1779,14 +1823,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
else
{
// Sample the current state
value = SampleState(bucket.CurrentState);
value = SampleState(context, bucket.CurrentState);
}
// Handle active transition blending
if (bucket.ActiveTransition)
{
// Sample the active transition destination state
const auto destinationState = SampleState(bucket.ActiveTransition->Destination);
const auto destinationState = SampleState(context, bucket.ActiveTransition->Destination);
// Perform blending
const float alpha = bucket.TransitionPosition / bucket.ActiveTransition->BlendDuration;
@@ -1794,6 +1838,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
}
bucket.LastUpdateFrame = context.CurrentFrameIndex;
context.NodePath.Pop();
#undef END_TRANSITION
break;
}
@@ -2248,7 +2293,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& value)
{
auto& context = Context.Get();
auto& context = *Context.Get();
if (context.ValueCache.TryGet(boxBase, value))
return;
switch (node->TypeID)

View File

@@ -413,10 +413,10 @@ bool Animation::Save(const StringView& path)
MemoryWriteStream stream(4096);
// Info
stream.WriteInt32(102);
stream.WriteInt32(103);
stream.WriteDouble(Data.Duration);
stream.WriteDouble(Data.FramesPerSecond);
stream.WriteBool(Data.EnableRootMotion);
stream.WriteByte((byte)Data.RootMotionFlags);
stream.WriteString(Data.RootNodeName, 13);
// Animation channels
@@ -532,17 +532,22 @@ Asset::LoadResult Animation::load()
int32 headerVersion = *(int32*)stream.GetPositionHandle();
switch (headerVersion)
{
case 100:
case 101:
case 102:
{
case 103:
stream.ReadInt32(&headerVersion);
stream.ReadDouble(&Data.Duration);
stream.ReadDouble(&Data.FramesPerSecond);
Data.EnableRootMotion = stream.ReadBool();
stream.ReadByte((byte*)&Data.RootMotionFlags);
stream.ReadString(&Data.RootNodeName, 13);
break;
case 100:
case 101:
case 102:
stream.ReadInt32(&headerVersion);
stream.ReadDouble(&Data.Duration);
stream.ReadDouble(&Data.FramesPerSecond);
Data.RootMotionFlags = stream.ReadBool() ? AnimationRootMotionFlags::RootPositionXZ : AnimationRootMotionFlags::None;
stream.ReadString(&Data.RootNodeName, 13);
break;
}
default:
stream.ReadDouble(&Data.Duration);
stream.ReadDouble(&Data.FramesPerSecond);

View File

@@ -67,7 +67,7 @@ void AnimationGraph::OnDependencyModified(BinaryAsset* asset)
#endif
bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop)
bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop, bool rootMotion)
{
if (!IsVirtual())
{
@@ -89,7 +89,7 @@ bool AnimationGraph::InitAsAnimation(SkinnedModel* baseModel, Animation* anim, b
rootNode.Type = GRAPH_NODE_MAKE_TYPE(9, 1);
rootNode.ID = 1;
rootNode.Values.Resize(1);
rootNode.Values[0] = (int32)RootMotionMode::NoExtraction;
rootNode.Values[0] = (int32)(rootMotion ? RootMotionExtraction::Enable : RootMotionExtraction::Ignore);
rootNode.Boxes.Resize(1);
rootNode.Boxes[0] = AnimGraphBox(&rootNode, 0, VariantType::Void);
auto& animNode = graph.Nodes[1];

View File

@@ -37,8 +37,9 @@ public:
/// <param name="baseModel">The base model asset.</param>
/// <param name="anim">The animation to play.</param>
/// <param name="loop">True if play animation in a loop.</param>
/// <param name="rootMotion">True if apply root motion. Otherwise it will be ignored.</param>
/// <returns>True if failed, otherwise false.</returns>
API_FUNCTION() bool InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop = true);
API_FUNCTION() bool InitAsAnimation(SkinnedModel* baseModel, Animation* anim, bool loop = true, bool rootMotion = false);
/// <summary>
/// Tries to load surface graph from the asset.

View File

@@ -277,6 +277,8 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
// Find asset in registry
if (Cache.FindAsset(path, info))
return true;
if (!FileSystem::FileExists(path))
return false;
PROFILE_CPU();
const auto extension = FileSystem::GetExtension(path).ToLower();

View File

@@ -393,34 +393,57 @@ bool JsonAsset::CreateInstance()
if (typeHandle)
{
auto& type = typeHandle.GetType();
// Ensure that object can deserialized
const ScriptingType::InterfaceImplementation* interface = type.GetInterface(ISerializable::TypeInitializer);
if (!interface)
{
LOG(Warning, "Cannot deserialize {0} from Json Asset because it doesn't implement ISerializable interface.", type.ToString());
return false;
}
auto modifier = Cache::ISerializeModifier.Get();
modifier->EngineBuild = DataEngineBuild;
// Create object
switch (type.Type)
{
case ScriptingTypes::Class:
case ScriptingTypes::Structure:
{
// Ensure that object can deserialized
const ScriptingType::InterfaceImplementation* interface = type.GetInterface(ISerializable::TypeInitializer);
if (!interface)
{
LOG(Warning, "Cannot deserialize {0} from Json Asset because it doesn't implement ISerializable interface.", type.ToString());
break;
}
// Allocate object
const auto instance = Allocator::Allocate(type.Size);
if (!instance)
return true;
Instance = instance;
InstanceType = typeHandle;
_dtor = type.Class.Dtor;
type.Class.Ctor(instance);
if (type.Type == ScriptingTypes::Class)
{
_dtor = type.Class.Dtor;
type.Class.Ctor(instance);
}
else
{
_dtor = type.Struct.Dtor;
type.Struct.Ctor(instance);
}
// Deserialize object
auto modifier = Cache::ISerializeModifier.Get();
modifier->EngineBuild = DataEngineBuild;
((ISerializable*)((byte*)instance + interface->VTableOffset))->Deserialize(*Data, modifier.Value);
break;
}
case ScriptingTypes::Script:
{
const ScriptingObjectSpawnParams params(Guid::New(), typeHandle);
const auto instance = type.Script.Spawn(params);
if (!instance)
return true;
Instance = instance;
_dtor = nullptr;
// Deserialize object
ToInterface<ISerializable>(instance)->Deserialize(*Data, modifier.Value);
break;
}
}
InstanceType = typeHandle;
}
return false;
@@ -441,13 +464,20 @@ void JsonAsset::DeleteInstance()
}
// C++ instance
if (!Instance || !_dtor)
if (!Instance)
return;
_dtor(Instance);
if (_dtor)
{
_dtor(Instance);
_dtor = nullptr;
Allocator::Free(Instance);
}
else
{
Delete((ScriptingObject*)Instance);
}
InstanceType = ScriptingTypeHandle();
Allocator::Free(Instance);
Instance = nullptr;
_dtor = nullptr;
}
#if USE_EDITOR

View File

@@ -139,7 +139,8 @@ public:
T* GetInstance() const
{
const_cast<JsonAsset*>(this)->CreateInstance();
return Instance && InstanceType.IsAssignableFrom(T::TypeInitializer) ? (T*)Instance : nullptr;
const ScriptingTypeHandle& type = T::TypeInitializer;
return Instance && type.IsAssignableFrom(InstanceType) ? (T*)Instance : nullptr;
}
public:

View File

@@ -0,0 +1,133 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Runtime.CompilerServices;
namespace FlaxEngine
{
/// <summary>
/// Json asset reference utility. References resource with a typed data type.
/// </summary>
/// <typeparam name="T">Type of the asset instance type.</typeparam>
#if FLAX_EDITOR
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.AssetRefEditor))]
#endif
public struct JsonAssetReference<T> : IComparable, IComparable<JsonAssetReference<T>>, IEquatable<JsonAssetReference<T>>
{
/// <summary>
/// Gets or sets the referenced asset.
/// </summary>
public JsonAsset Asset;
/// <summary>
/// Gets the instance of the serialized object from the json asset data. Cached internally.
/// </summary>
public T Instance => (T)Asset?.Instance;
/// <summary>
/// Initializes a new instance of the <see cref="JsonAssetReference{T}"/> structure.
/// </summary>
/// <param name="asset">The Json Asset.</param>
public JsonAssetReference(JsonAsset asset)
{
Asset = asset;
}
/// <summary>
/// Implicit cast operator.
/// </summary>
public static implicit operator JsonAsset(JsonAssetReference<T> value)
{
return value.Asset;
}
/// <summary>
/// Implicit cast operator.
/// </summary>
public static implicit operator IntPtr(JsonAssetReference<T> value)
{
return Object.GetUnmanagedPtr(value.Asset);
}
/// <summary>
/// Implicit cast operator.
/// </summary>
public static implicit operator JsonAssetReference<T>(JsonAsset value)
{
return new JsonAssetReference<T>(value);
}
/// <summary>
/// Implicit cast operator.
/// </summary>
public static implicit operator JsonAssetReference<T>(IntPtr valuePtr)
{
return new JsonAssetReference<T>(Object.FromUnmanagedPtr(valuePtr) as JsonAsset);
}
/// <summary>
/// Checks if the object exists (reference is not null and the unmanaged object pointer is valid).
/// </summary>
/// <param name="obj">The object to check.</param>
/// <returns>True if object is valid, otherwise false.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator bool(JsonAssetReference<T> obj)
{
return obj.Asset;
}
/// <summary>
/// Checks whether the two objects are equal.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(JsonAssetReference<T> left, JsonAssetReference<T> right)
{
return left.Asset == right.Asset;
}
/// <summary>
/// Checks whether the two objects are not equal.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(JsonAssetReference<T> left, JsonAssetReference<T> right)
{
return left.Asset != right.Asset;
}
/// <inheritdoc />
public bool Equals(JsonAssetReference<T> other)
{
return Asset == other.Asset;
}
/// <inheritdoc />
public int CompareTo(JsonAssetReference<T> other)
{
return Object.GetUnmanagedPtr(Asset).CompareTo(Object.GetUnmanagedPtr(other.Asset));
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return obj is JsonAssetReference<T> other && Asset == other.Asset;
}
/// <inheritdoc />
public override string ToString()
{
return Asset?.ToString();
}
/// <inheritdoc />
public int CompareTo(object obj)
{
return obj is JsonAssetReference<T> other ? CompareTo(other) : 1;
}
/// <inheritdoc />
public override int GetHashCode()
{
return (Asset != null ? Asset.GetHashCode() : 0);
}
}
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Content/JsonAsset.h"
#include "Engine/Content/AssetReference.h"
/// <summary>
/// Json asset reference utility. References resource with a typed data type.
/// </summary>
/// <typeparam name="T">Type of the asset instance type.</typeparam>
template<typename T>
API_STRUCT(NoDefault, Template, MarshalAs=JsonAsset*) struct JsonAssetReference : AssetReference<JsonAsset>
{
JsonAssetReference() = default;
JsonAssetReference(JsonAsset* asset)
{
OnSet(asset);
}
/// <summary>
/// Gets the deserialized native object instance of the given type. Returns null if asset is not loaded or loaded object has different type.
/// </summary>
/// <returns>The asset instance object or null.</returns>
FORCE_INLINE T* GetInstance() const
{
return _asset ? Get()->template GetInstance<T>() : nullptr;
}
JsonAssetReference& operator=(JsonAsset* asset) noexcept
{
OnSet(asset);
return *this;
}
operator JsonAsset*() const
{
return Get();
}
};

View File

@@ -58,8 +58,8 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
Locker.Lock();
// Try fast lookup
FlaxStorage* result;
if (!StorageMap.TryGet(path, result))
FlaxStorage* storage;
if (!StorageMap.TryGet(path, storage))
{
// Detect storage type and create object
const bool isPackage = path.EndsWith(StringView(PACKAGE_FILES_EXTENSION));
@@ -67,39 +67,42 @@ FlaxStorageReference ContentStorageManager::GetStorage(const StringView& path, b
{
auto package = New<FlaxPackage>(path);
Packages.Add(package);
result = package;
storage = package;
}
else
{
auto file = New<FlaxFile>(path);
Files.Add(file);
result = file;
storage = file;
}
// Register storage container
StorageMap.Add(path, result);
StorageMap.Add(path, storage);
}
// Build reference (before releasing the lock so ContentStorageSystem::Job won't delete it when running from async thread)
FlaxStorageReference result(storage);
Locker.Unlock();
if (loadIt)
{
// Initialize storage container
result->LockChunks();
const bool loadFailed = result->Load();
result->UnlockChunks();
storage->LockChunks();
const bool loadFailed = storage->Load();
storage->UnlockChunks();
if (loadFailed)
{
LOG(Error, "Failed to load {0}.", path);
Locker.Lock();
StorageMap.Remove(path);
if (result->IsPackage())
Packages.Remove((FlaxPackage*)result);
if (storage->IsPackage())
Packages.Remove((FlaxPackage*)storage);
else
Files.Remove((FlaxFile*)result);
Files.Remove((FlaxFile*)storage);
Locker.Unlock();
Delete(result);
return nullptr;
result = nullptr;
Delete(storage);
}
}

View File

@@ -211,7 +211,13 @@ FlaxStorage::~FlaxStorage()
#if USE_EDITOR
// Ensure to close any outstanding file handles to prevent file locking in case it failed to load
_file.DeleteAll();
Array<FileReadStream*> streams;
_file.GetValues(streams);
for (FileReadStream* stream : streams)
{
if (stream)
Delete(stream);
}
#endif
}
@@ -1266,7 +1272,6 @@ bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data)
}
#if ASSETS_LOADING_EXTRA_VERIFICATION
// Validate loaded header (asset ID and type ID must be the same)
if (e.ID != data.Header.ID)
{
@@ -1276,7 +1281,6 @@ bool FlaxStorage::LoadAssetHeader(const Entry& e, AssetInitData& data)
{
LOG(Error, "Loading asset header data mismatch! Expected Type Name: {0}, loaded header: {1}.\nSource: {2}", e.TypeName, data.Header.ToString(), ToString());
}
#endif
return false;
@@ -1339,7 +1343,14 @@ bool FlaxStorage::CloseFileHandles()
return true; // Failed, someone is still accessing the file
// Close file handles (from all threads)
_file.DeleteAll();
Array<FileReadStream*> streams;
_file.GetValues(streams);
for (FileReadStream* stream : streams)
{
if (stream)
Delete(stream);
}
_file.Clear();
return false;
}

View File

@@ -93,7 +93,7 @@ protected:
CriticalSection _loadLocker;
// Storage
ThreadLocalObject<FileReadStream> _file;
ThreadLocal<FileReadStream*> _file;
Array<FlaxChunk*> _chunks;
// Metadata

View File

@@ -58,17 +58,17 @@ public:
return _storage != nullptr;
}
FORCE_INLINE bool operator ==(const FlaxStorageReference& other) const
FORCE_INLINE bool operator==(const FlaxStorageReference& other) const
{
return _storage == other._storage;
}
FORCE_INLINE bool operator !=(const FlaxStorageReference& other) const
FORCE_INLINE bool operator!=(const FlaxStorageReference& other) const
{
return _storage != other._storage;
}
FORCE_INLINE FlaxStorage* operator ->() const
FORCE_INLINE FlaxStorage* operator->() const
{
return _storage;
}

View File

@@ -234,7 +234,6 @@ bool AssetsImportingManager::Create(const String& tag, const StringView& outputP
LOG(Warning, "Cannot find asset creator object for tag \'{0}\'.", tag);
return true;
}
return Create(creator->Callback, outputPath, assetId, arg);
}

View File

@@ -20,7 +20,7 @@ CreateAssetResult CreateAnimationGraph::Create(CreateAssetContext& context)
rootNode.Type = GRAPH_NODE_MAKE_TYPE(9, 1);
rootNode.ID = 1;
rootNode.Values.Resize(1);
rootNode.Values[0] = (int32)RootMotionMode::NoExtraction;
rootNode.Values[0] = (int32)RootMotionExtraction::NoExtraction;
rootNode.Boxes.Resize(1);
rootNode.Boxes[0] = AnimGraphBox(&rootNode, 0, VariantType::Void);

View File

@@ -113,7 +113,7 @@ private:
/// <summary>
/// Asset importer entry
/// </summary>
struct AssetImporter
struct FLAXENGINE_API AssetImporter
{
public:
/// <summary>
@@ -135,7 +135,7 @@ public:
/// <summary>
/// Asset creator entry
/// </summary>
struct AssetCreator
struct FLAXENGINE_API AssetCreator
{
public:
/// <summary>

View File

@@ -250,6 +250,16 @@ public:
return _count == 0;
}
/// <summary>
/// Determines if given index is valid.
/// </summary>
/// <param name="index">The index.</param>
/// <returns><c>true</c> if is valid a index; otherwise, <c>false</c>.</returns>
bool IsValidIndex(int32 index) const
{
return index < _count && index >= 0;
}
/// <summary>
/// Gets the pointer to the first item in the collection (linear allocation).
/// </summary>

View File

@@ -5,11 +5,14 @@
#include "Engine/Threading/ThreadLocal.h"
// Use a cached storage for the sorting (one per thread to reduce locking)
ThreadLocal<Sorting::SortingStack> SortingStacks;
ThreadLocal<Sorting::SortingStack*> SortingStacks;
Sorting::SortingStack& Sorting::SortingStack::Get()
{
return SortingStacks.Get();
SortingStack*& stack = SortingStacks.Get();
if (!stack)
stack = New<SortingStack>();
return *stack;
}
Sorting::SortingStack::SortingStack()

View File

@@ -119,7 +119,7 @@ namespace FlaxEditor.Content.Settings
/// <summary>
/// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).
/// </summary>
[EditorOrder(1100), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")]
[EditorOrder(1500), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")]
public Dictionary<string, JsonAsset> CustomSettings;
#if FLAX_EDITOR || PLATFORM_WINDOWS

View File

@@ -6,6 +6,8 @@
#include "Engine/Graphics/Enums.h"
#include "Engine/Graphics/PostProcessSettings.h"
class FontAsset;
/// <summary>
/// Graphics rendering settings.
/// </summary>
@@ -13,6 +15,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE_MINIMAL(GraphicsSettings);
public:
/// <summary>
/// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts.
@@ -118,6 +121,12 @@ public:
API_FIELD(Attributes="EditorOrder(10000), EditorDisplay(\"Post Process Settings\", EditorDisplayAttribute.InlineStyle)")
PostProcessSettings PostProcessSettings;
/// <summary>
/// The list of fallback fonts used for text rendering. Ignored if empty.
/// </summary>
API_FIELD(Attributes="EditorOrder(5000), EditorDisplay(\"Text\")")
Array<AssetReference<FontAsset>> FallbackFonts;
private:
/// <summary>
/// Renamed UeeHDRProbes into UseHDRProbes

View File

@@ -104,6 +104,16 @@ namespace FlaxEngine
GetPlanesFromMatrix(ref pMatrix, out pNear, out pFar, out pLeft, out pRight, out pTop, out pBottom);
}
/// <summary>
/// Creates a new instance of BoundingFrustum.
/// </summary>
/// <param name="matrix">Combined matrix that usually takes view × projection matrix.</param>
public BoundingFrustum(ref Matrix matrix)
{
pMatrix = matrix;
GetPlanesFromMatrix(ref pMatrix, out pNear, out pFar, out pLeft, out pRight, out pTop, out pBottom);
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>

View File

@@ -92,6 +92,21 @@ namespace FlaxEngine
/// </summary>
public float MaxColorComponent => Mathf.Max(Mathf.Max(R, G), B);
/// <summary>
/// Gets a minimum component value (max of r,g,b,a).
/// </summary>
public float MinValue => Math.Min(R, Math.Min(G, Math.Min(B, A)));
/// <summary>
/// Gets a maximum component value (min of r,g,b,a).
/// </summary>
public float MaxValue => Math.Max(R, Math.Max(G, Math.Max(B, A)));
/// <summary>
/// Gets a sum of the component values.
/// </summary>
public float ValuesSum => R + G + B + A;
/// <summary>
/// Constructs a new Color with given r,g,b,a component.
/// </summary>

View File

@@ -400,10 +400,10 @@ DebugDrawCall WriteList(int32& vertexCounter, const Array<DebugLine>& list)
drawCall.StartVertex = vertexCounter;
drawCall.VertexCount = list.Count() * 2;
vertexCounter += drawCall.VertexCount;
Vertex* dst = DebugDrawVB->WriteReserve<Vertex>(drawCall.VertexCount);
Vertex* dst = DebugDrawVB->WriteReserve<Vertex>(list.Count() * 2);
for (int32 i = 0, j = 0; i < list.Count(); i++)
{
const DebugLine& l = list[i];
const DebugLine& l = list.Get()[i];
dst[j++] = { l.Start, l.Color };
dst[j++] = { l.End, l.Color };
}
@@ -416,10 +416,10 @@ DebugDrawCall WriteList(int32& vertexCounter, const Array<DebugTriangle>& list)
drawCall.StartVertex = vertexCounter;
drawCall.VertexCount = list.Count() * 3;
vertexCounter += drawCall.VertexCount;
Vertex* dst = DebugDrawVB->WriteReserve<Vertex>(drawCall.VertexCount);
Vertex* dst = DebugDrawVB->WriteReserve<Vertex>(list.Count() * 3);
for (int32 i = 0, j = 0; i < list.Count(); i++)
{
const DebugTriangle& l = list[i];
const DebugTriangle& l = list.Get()[i];
dst[j++] = { l.V0, l.Color };
dst[j++] = { l.V1, l.Color };
dst[j++] = { l.V2, l.Color };
@@ -923,11 +923,40 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo
}
}
void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest)
void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, float size, float duration, bool depthTest)
{
const auto rot = Quaternion::FromDirection(direction.GetNormalized());
const Vector3 up = (rot * Vector3::Up);
const Vector3 forward = (rot * Vector3::Forward);
const Vector3 right = (rot * Vector3::Right);
const float sizeHalf = size * 0.5f;
DrawLine(origin, origin + up * sizeHalf + up, Color::Green, duration, depthTest);
DrawLine(origin, origin + forward * sizeHalf + forward, Color::Blue, duration, depthTest);
DrawLine(origin, origin + right * sizeHalf + right, Color::Red, duration, depthTest);
}
void DebugDraw::DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest)
{
auto dir = origin + direction;
if (dir.IsNanOrInfinity())
return;
DrawLine(origin, origin + direction, color, duration, depthTest);
}
void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float length, float duration, bool depthTest)
{
if (isnan(length) || isinf(length))
return;
DrawLine(origin, origin + (direction.GetNormalized() * length), color, duration, depthTest);
}
void DebugDraw::DrawRay(const Ray& ray, const Color& color, float length, float duration, bool depthTest)
{
if (isnan(length) || isinf(length))
return;
DrawLine(ray.Position, ray.Position + (ray.Direction.GetNormalized() * length), color, duration, depthTest);
}
void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest)
{
const Float3 startF = start - Context->Origin, endF = end - Context->Origin;
@@ -1940,15 +1969,15 @@ void DebugDraw::DrawWireArc(const Vector3& position, const Quaternion& orientati
DrawLine(prevPos, world.GetTranslation(), color, duration, depthTest);
}
void DebugDraw::DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, const Color& color, float duration, bool depthTest)
void DebugDraw::DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, float capScale, const Color& color, float duration, bool depthTest)
{
Float3 direction, up, right;
Float3::Transform(Float3::Forward, orientation, direction);
Float3::Transform(Float3::Up, orientation, up);
Float3::Transform(Float3::Right, orientation, right);
const Vector3 end = position + direction * (100.0f * scale);
const Vector3 capEnd = position + direction * (70.0f * scale);
const float arrowSidesRatio = scale * 30.0f;
const Vector3 capEnd = end - (direction * (100 * Math::Min(capScale, scale * 0.5f)));
const float arrowSidesRatio = Math::Min(capScale, scale * 0.5f) * 30.0f;
DrawLine(position, end, color, duration, depthTest);
DrawLine(end, capEnd + up * arrowSidesRatio, color, duration, depthTest);

View File

@@ -31,6 +31,17 @@ namespace FlaxEngine
{
}
/// <summary>
/// Draws the lines axis from direction.
/// </summary>
/// <param name="origin">The origin of the line.</param>
/// <param name="direction">The direction of the line.</param>
/// <param name="color">The color.</param>
/// <param name="Size">The size of the axis.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
public static void DrawAxisFromDirection(Vector3 origin, Vector3 direction, Color color ,float Size = 100.0f, float duration = 0.0f, bool depthTest = true){}
/// <summary>
/// Draws the line in a direction.
/// </summary>
@@ -39,7 +50,28 @@ namespace FlaxEngine
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
public static void DrawRay(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true)
public static void DrawDirection(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true){}
/// <summary>
/// Draws the line in a direction.
/// </summary>
/// <param name="origin">The origin of the line.</param>
/// <param name="direction">The direction of the line.</param>
/// <param name="color">The color.</param>
/// <param name="length">The length of the ray.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
public static void DrawRay(Vector3 origin, Vector3 direction, Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true){}
/// <summary>
/// Draws the line in a direction.
/// </summary>
/// <param name="ray">The ray.</param>
/// <param name="color">The color.</param>
/// <param name="length">The length of the ray.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
public static void DrawRay(Ray ray,Color color, float length = 3.402823466e+38f, float duration = 0.0f, bool depthTest = true)
{
}
@@ -218,10 +250,11 @@ namespace FlaxEngine
/// <param name="position">The arrow origin position.</param>
/// <param name="orientation">The orientation (defines the arrow direction).</param>
/// <param name="scale">The arrow scale (used to adjust the arrow size).</param>
/// <param name="capScale">The arrow cap scale.</param>
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
public static void DrawWireArrow(Vector3 position, Quaternion orientation, float scale, Color color, float duration = 0.0f, bool depthTest = true)
public static void DrawWireArrow(Vector3 position, Quaternion orientation, float scale, float capScale, Color color, float duration = 0.0f, bool depthTest = true)
{
}

View File

@@ -24,7 +24,6 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
#if USE_EDITOR
/// <summary>
/// Allocates the context for Debug Drawing. Can be use to redirect debug shapes collecting to a separate container (instead of global state).
/// </summary>
@@ -49,7 +48,6 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// </summary>
/// <param name="context">The context or null.</param>
API_FUNCTION() static void SetContext(void* context);
#endif
/// <summary>
@@ -69,6 +67,16 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="drawScenes">True if draw all debug shapes from scenes too or false if draw just from specified actor list.</param>
API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes);
/// <summary>
/// Draws the lines axis from direction.
/// </summary>
/// <param name="origin">The origin of the line.</param>
/// <param name="direction">The direction of the line.</param>
/// <param name="size">The size of the axis.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, float size = 100.0f, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the line in a direction.
/// </summary>
@@ -77,7 +85,28 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawDirection(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the line in a direction.
/// </summary>
/// <param name="origin">The origin of the line.</param>
/// <param name="direction">The direction of the line.</param>
/// <param name="color">The color.</param>
/// <param name="length">The length of the ray.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color = Color::White, float length = MAX_float, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the line in a direction.
/// </summary>
/// <param name="ray">The ray.</param>
/// <param name="color">The color.</param>
/// <param name="length">The length of the ray.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawRay(const Ray& ray, const Color& color = Color::White, float length = MAX_float, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the line.
@@ -87,7 +116,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawLine(const Vector3& start, const Vector3& end, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the line.
@@ -108,7 +137,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawLines(const Span<Float3>& lines, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawLines(const Span<Float3>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
@@ -118,7 +147,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawLines(const Array<Float3, HeapAllocation>& lines, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawLines(const Array<Float3, HeapAllocation>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
@@ -128,7 +157,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawLines(const Span<Double3>& lines, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawLines(const Span<Double3>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
@@ -138,7 +167,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawLines(const Array<Double3, HeapAllocation>& lines, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawLines(const Array<Double3, HeapAllocation>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws a Bezier curve.
@@ -150,7 +179,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The line color</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the circle.
@@ -161,7 +190,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawCircle(const Vector3& position, const Float3& normal, float radius, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawCircle(const Vector3& position, const Float3& normal, float radius, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangle.
@@ -172,7 +201,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangle.
@@ -183,7 +212,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles.
@@ -192,7 +221,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangles(const Span<Float3>& vertices, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangles(const Span<Float3>& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles.
@@ -202,7 +231,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangles(const Span<Float3>& vertices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangles(const Span<Float3>& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles.
@@ -211,7 +240,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawTriangles(const Array<Float3, HeapAllocation>& vertices, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawTriangles(const Array<Float3, HeapAllocation>& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles.
@@ -221,7 +250,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawTriangles(const Array<Float3, HeapAllocation>& vertices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawTriangles(const Array<Float3, HeapAllocation>& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles using the given index buffer.
@@ -231,7 +260,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles using the given index buffer.
@@ -242,7 +271,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles using the given index buffer.
@@ -252,7 +281,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawTriangles(const Array<Float3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawTriangles(const Array<Float3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles using the given index buffer.
@@ -263,7 +292,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawTriangles(const Array<Float3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawTriangles(const Array<Float3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles.
@@ -272,7 +301,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangles(const Span<Double3>& vertices, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangles(const Span<Double3>& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles.
@@ -282,7 +311,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangles(const Span<Double3>& vertices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangles(const Span<Double3>& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles.
@@ -291,7 +320,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawTriangles(const Array<Double3, HeapAllocation>& vertices, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawTriangles(const Array<Double3, HeapAllocation>& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles.
@@ -301,7 +330,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawTriangles(const Array<Double3, HeapAllocation>& vertices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawTriangles(const Array<Double3, HeapAllocation>& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles using the given index buffer.
@@ -311,7 +340,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles using the given index buffer.
@@ -322,7 +351,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles using the given index buffer.
@@ -332,7 +361,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawTriangles(const Array<Double3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawTriangles(const Array<Double3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the triangles using the given index buffer.
@@ -343,7 +372,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawTriangles(const Array<Double3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Matrix& transform, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawTriangles(const Array<Double3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangles.
@@ -352,7 +381,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireTriangles(const Span<Float3>& vertices, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireTriangles(const Span<Float3>& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangles.
@@ -361,7 +390,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawWireTriangles(const Array<Float3, HeapAllocation>& vertices, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawWireTriangles(const Array<Float3, HeapAllocation>& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangles using the given index buffer.
@@ -371,7 +400,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireTriangles(const Span<Float3>& vertices, const Span<int32>& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangles using the given index buffer.
@@ -381,7 +410,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawWireTriangles(const Array<Float3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawWireTriangles(const Array<Float3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangles.
@@ -390,7 +419,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireTriangles(const Span<Double3>& vertices, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireTriangles(const Span<Double3>& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangles.
@@ -399,7 +428,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawWireTriangles(const Array<Double3, HeapAllocation>& vertices, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawWireTriangles(const Array<Double3, HeapAllocation>& vertices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangles using the given index buffer.
@@ -409,7 +438,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireTriangles(const Span<Double3>& vertices, const Span<int32>& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe triangles using the given index buffer.
@@ -419,7 +448,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
static void DrawWireTriangles(const Array<Double3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color, float duration = 0.0f, bool depthTest = true);
static void DrawWireTriangles(const Array<Double3, HeapAllocation>& vertices, const Array<int32, HeapAllocation>& indices, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe box.
@@ -428,7 +457,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireBox(const BoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireBox(const BoundingBox& box, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe frustum.
@@ -437,7 +466,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireFrustum(const BoundingFrustum& frustum, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireFrustum(const BoundingFrustum& frustum, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe box.
@@ -446,7 +475,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireBox(const OrientedBoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireBox(const OrientedBoundingBox& box, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe sphere.
@@ -455,7 +484,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireSphere(const BoundingSphere& sphere, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireSphere(const BoundingSphere& sphere, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the sphere.
@@ -464,7 +493,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawSphere(const BoundingSphere& sphere, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawSphere(const BoundingSphere& sphere, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the tube.
@@ -476,7 +505,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe tube.
@@ -488,7 +517,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the cylinder.
@@ -500,7 +529,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe cylinder.
@@ -512,7 +541,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireCylinder(const Vector3& position, const Quaternion& orientation, float radius, float height, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the cone.
@@ -525,7 +554,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe cone.
@@ -538,7 +567,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireCone(const Vector3& position, const Quaternion& orientation, float radius, float angleXY, float angleXZ, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the arc.
@@ -550,7 +579,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe arc.
@@ -562,7 +591,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireArc(const Vector3& position, const Quaternion& orientation, float radius, float angle, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the wireframe arrow.
@@ -570,10 +599,11 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="position">The arrow origin position.</param>
/// <param name="orientation">The orientation (defines the arrow direction).</param>
/// <param name="scale">The arrow scale (used to adjust the arrow size).</param>
/// <param name="capScale">The arrow cap scale.</param>
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, float capScale, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the box.
@@ -582,7 +612,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawBox(const BoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawBox(const BoundingBox& box, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the box.
@@ -591,7 +621,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawBox(const OrientedBoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true);
API_FUNCTION() static void DrawBox(const OrientedBoundingBox& box, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the text on a screen (2D).
@@ -601,7 +631,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Float2& position, const Color& color, int32 size = 20, float duration = 0.0f);
API_FUNCTION() static void DrawText(const StringView& text, const Float2& position, const Color& color = Color::White, int32 size = 20, float duration = 0.0f);
/// <summary>
/// Draws the text (3D) that automatically faces the camera.
@@ -612,7 +642,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="scale">The text scale.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size = 32, float duration = 0.0f, float scale = 1.0f);
API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, const Color& color = Color::White, int32 size = 32, float duration = 0.0f, float scale = 1.0f);
/// <summary>
/// Draws the text (3D).
@@ -622,39 +652,45 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="color">The color.</param>
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size = 32, float duration = 0.0f);
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f);
};
#define DEBUG_DRAW_RAY(origin, direction, color, duration, depthTest) DebugDraw::DrawRay(origin, direction, color, duration, depthTest)
#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest)
#define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) DebugDraw::DrawLines(lines, transform, color, duration, depthTest)
#define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) DebugDraw::DrawBezier(p1, p2, p3, p4, color, duration, depthTest)
#define DEBUG_DRAW_CIRCLE(position, normal, radius, color, duration, depthTest) DebugDraw::DrawCircle(position, normal, radius, color, duration, depthTest)
#define DEBUG_DRAW_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawTriangle(v0, v1, v2, color, duration, depthTest)
#define DEBUG_DRAW_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, color, duration, depthTest)
#define DEBUG_DRAW_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, color, duration, depthTest)
#define DEBUG_DRAW_TRIANGLES_EX2(vertices, indices, transform, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, transform, color, duration, depthTest)
#define DEBUG_DRAW_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawSphere(sphere, color, duration, depthTest)
#define DEBUG_DRAW_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawTube(position, orientation, radius, length, color, duration, depthTest)
#define DEBUG_DRAW_BOX(box, color, duration, depthTest) DebugDraw::DrawBox(box, color, duration, depthTest)
#define DEBUG_DRAW_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawCylinder(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) DebugDraw::DrawCone(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest)
#define DEBUG_DRAW_ARC(position, orientation, radius, angle, color, duration, depthTest) DebugDraw::DrawArc(position, orientation, radius, angle, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawWireTriangle(v0, v1, v2, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, indices, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_BOX(box, color, duration, depthTest) DebugDraw::DrawWireBox(box, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_FRUSTUM(frustum, color, duration, depthTest) DebugDraw::DrawWireFrustum(frustum, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawWireSphere(sphere, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, length, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawWireCylinder(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) DebugDraw::DrawWireCone(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARC(position, orientation, radius, angle, color, duration, depthTest) DebugDraw::DrawWireArc(position, orientation, radius, angle, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireArrow(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_TEXT(text, position, color, size, duration) DebugDraw::DrawText(text, position, color, size, duration)
#define DEBUG_DRAW_AXIS_FROM_DIRECTION(origin, direction, size, duration, depthTest) DebugDraw::DrawAxisFromDirection(origin, direction, size, duration, depthTest);
#define DEBUG_DRAW_DIRECTION(origin, direction, color, duration, depthTest) DebugDraw::DrawDirection(origin, direction, color, duration, depthTest);
#define DEBUG_DRAW_RAY(origin, direction, color, length, duration, depthTest) DebugDraw::DrawRay(origin, direction, color, length, duration, depthTest);
#define DEBUG_DRAW_RAY(ray, color, length, duration, depthTest) DebugDraw::DrawRay(ray, color, length, duration, depthTest);
#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest)
#define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) DebugDraw::DrawLines(lines, transform, color, duration, depthTest)
#define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) DebugDraw::DrawBezier(p1, p2, p3, p4, color, duration, depthTest)
#define DEBUG_DRAW_CIRCLE(position, normal, radius, color, duration, depthTest) DebugDraw::DrawCircle(position, normal, radius, color, duration, depthTest)
#define DEBUG_DRAW_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawTriangle(v0, v1, v2, color, duration, depthTest)
#define DEBUG_DRAW_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, color, duration, depthTest)
#define DEBUG_DRAW_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, color, duration, depthTest)
#define DEBUG_DRAW_TRIANGLES_EX2(vertices, indices, transform, color, duration, depthTest) DebugDraw::DrawTriangles(vertices, indices, transform, color, duration, depthTest)
#define DEBUG_DRAW_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawSphere(sphere, color, duration, depthTest)
#define DEBUG_DRAW_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawTube(position, orientation, radius, length, color, duration, depthTest)
#define DEBUG_DRAW_BOX(box, color, duration, depthTest) DebugDraw::DrawBox(box, color, duration, depthTest)
#define DEBUG_DRAW_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawCylinder(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) DebugDraw::DrawCone(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest)
#define DEBUG_DRAW_ARC(position, orientation, radius, angle, color, duration, depthTest) DebugDraw::DrawArc(position, orientation, radius, angle, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawWireTriangle(v0, v1, v2, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, indices, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_BOX(box, color, duration, depthTest) DebugDraw::DrawWireBox(box, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_FRUSTUM(frustum, color, duration, depthTest) DebugDraw::DrawWireFrustum(frustum, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawWireSphere(sphere, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, length, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawWireCylinder(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest) DebugDraw::DrawWireCone(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARC(position, orientation, radius, angle, color, duration, depthTest) DebugDraw::DrawWireArc(position, orientation, radius, angle, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, capScale, color, duration, depthTest) DebugDraw::DrawWireArrow(position, orientation, scale, capScale, color, duration, depthTest)
#define DEBUG_DRAW_TEXT(text, position, color, size, duration) DebugDraw::DrawText(text, position, color, size, duration)
#else
#define DEBUG_DRAW_AXIS_FROM_DIRECTION(origin, direction, size, duration, depthTest)
#define DEBUG_DRAW_DIRECTION(origin, direction,color,duration, depthTest)
#define DEBUG_DRAW_RAY(ray, color, length, duration, depthTest)
#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest)
#define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest)
#define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest)
@@ -679,7 +715,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARC(position, orientation, radius, angle, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, capScale, color, duration, depthTest)
#define DEBUG_DRAW_TEXT(text, position, color, size, duration)
#endif

View File

@@ -214,9 +214,6 @@ int32 Engine::Main(const Char* cmdLine)
Time::OnEndDraw();
FrameMark;
}
// Collect physics simulation results (does nothing if Simulate hasn't been called in the previous loop step)
Physics::CollectResults();
}
// Call on exit event
@@ -288,6 +285,9 @@ void Engine::OnLateFixedUpdate()
// Update services
EngineService::OnLateFixedUpdate();
// Collect physics simulation results (does nothing if Simulate hasn't been called in the previous loop step)
Physics::CollectResults();
}
void Engine::OnUpdate()

View File

@@ -72,9 +72,6 @@ void EngineService::OnInit()
// Init services from front to back
auto& services = GetServices();
#if TRACY_ENABLE
Char nameBuffer[100];
#endif
for (int32 i = 0; i < services.Count(); i++)
{
const auto service = services[i];
@@ -82,6 +79,7 @@ void EngineService::OnInit()
#if TRACY_ENABLE
ZoneScoped;
int32 nameBufferLength = 0;
Char nameBuffer[100];
for (int32 j = 0; j < name.Length(); j++)
if (name[j] != ' ')
nameBuffer[nameBufferLength++] = name[j];
@@ -114,6 +112,18 @@ void EngineService::OnDispose()
const auto service = services[i];
if (service->IsInitialized)
{
#if TRACY_ENABLE
ZoneScoped;
const StringView name(service->Name);
int32 nameBufferLength = 0;
Char nameBuffer[100];
for (int32 j = 0; j < name.Length(); j++)
if (name[j] != ' ')
nameBuffer[nameBufferLength++] = name[j];
Platform::MemoryCopy(nameBuffer + nameBufferLength, TEXT("::Dispose"), 10 * sizeof(Char));
nameBufferLength += 10;
ZoneName(nameBuffer, nameBufferLength);
#endif
service->IsInitialized = false;
service->Dispose();
}

View File

@@ -249,7 +249,7 @@ namespace FlaxEngine.Interop
/// <param name="src">The input array.</param>
/// <param name="convertFunc">Converter callback.</param>
/// <returns>The output array.</returns>
public static TDst[] ConvertArray<TSrc, TDst>(Span<TSrc> src, Func<TSrc, TDst> convertFunc)
public static TDst[] ConvertArray<TSrc, TDst>(this Span<TSrc> src, Func<TSrc, TDst> convertFunc)
{
TDst[] dst = new TDst[src.Length];
for (int i = 0; i < src.Length; i++)
@@ -265,7 +265,7 @@ namespace FlaxEngine.Interop
/// <param name="src">The input array.</param>
/// <param name="convertFunc">Converter callback.</param>
/// <returns>The output array.</returns>
public static TDst[] ConvertArray<TSrc, TDst>(TSrc[] src, Func<TSrc, TDst> convertFunc)
public static TDst[] ConvertArray<TSrc, TDst>(this TSrc[] src, Func<TSrc, TDst> convertFunc)
{
TDst[] dst = new TDst[src.Length];
for (int i = 0; i < src.Length; i++)

View File

@@ -12,6 +12,7 @@
#include "Engine/Debug/Exceptions/InvalidOperationException.h"
#include "Engine/Debug/Exceptions/ArgumentNullException.h"
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Enums.h"
#include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Threading/Threading.h"
@@ -81,33 +82,10 @@ bool GPUBufferDescription::Equals(const GPUBufferDescription& other) const
String GPUBufferDescription::ToString() const
{
// TODO: add tool to Format to string
String flags;
if (Flags == GPUBufferFlags::None)
{
flags = TEXT("None");
}
else
{
// TODO: create tool to auto convert flag enums to string
#define CONVERT_FLAGS_FLAGS_2_STR(value) if (EnumHasAnyFlags(Flags, GPUBufferFlags::value)) { if (flags.HasChars()) flags += TEXT('|'); flags += TEXT(#value); }
CONVERT_FLAGS_FLAGS_2_STR(ShaderResource);
CONVERT_FLAGS_FLAGS_2_STR(VertexBuffer);
CONVERT_FLAGS_FLAGS_2_STR(IndexBuffer);
CONVERT_FLAGS_FLAGS_2_STR(UnorderedAccess);
CONVERT_FLAGS_FLAGS_2_STR(Append);
CONVERT_FLAGS_FLAGS_2_STR(Counter);
CONVERT_FLAGS_FLAGS_2_STR(Argument);
CONVERT_FLAGS_FLAGS_2_STR(Structured);
#undef CONVERT_FLAGS_FLAGS_2_STR
}
return String::Format(TEXT("Size: {0}, Stride: {1}, Flags: {2}, Format: {3}, Usage: {4}"),
Size,
Stride,
flags,
ScriptingEnum::ToStringFlags(Flags),
ScriptingEnum::ToString(Format),
(int32)Usage);
}
@@ -212,7 +190,7 @@ GPUBuffer* GPUBuffer::ToStagingUpload() const
bool GPUBuffer::Resize(uint32 newSize)
{
// Validate input
PROFILE_CPU();
if (!IsAllocated())
{
Log::InvalidOperationException(TEXT("Buffer.Resize"));
@@ -236,12 +214,12 @@ bool GPUBuffer::DownloadData(BytesContainer& result)
LOG(Warning, "Cannot download GPU buffer data from an empty buffer.");
return true;
}
if (_desc.Usage == GPUResourceUsage::StagingReadback || _desc.Usage == GPUResourceUsage::Dynamic)
{
// Use faster path for staging resources
return GetData(result);
}
PROFILE_CPU();
// Ensure not running on main thread
if (IsInMainThread())
@@ -358,6 +336,7 @@ Task* GPUBuffer::DownloadDataAsync(BytesContainer& result)
bool GPUBuffer::GetData(BytesContainer& output)
{
PROFILE_CPU();
void* mapped = Map(GPUResourceMapMode::Read);
if (!mapped)
return true;
@@ -368,6 +347,7 @@ bool GPUBuffer::GetData(BytesContainer& output)
void GPUBuffer::SetData(const void* data, uint32 size)
{
PROFILE_CPU();
if (size == 0 || data == nullptr)
{
Log::ArgumentNullException(TEXT("Buffer.SetData"));

View File

@@ -8,6 +8,7 @@
#include "Engine/Core/Config/GraphicsSettings.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Render2D/Font.h"
bool Graphics::UseVSync = false;
Quality Graphics::AAQuality = Quality::Medium;
@@ -69,6 +70,9 @@ void GraphicsSettings::Apply()
Graphics::GIQuality = GIQuality;
Graphics::PostProcessSettings = ::PostProcessSettings();
Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f);
#if !USE_EDITOR // OptionsModule handles fallback fonts in Editor
Font::FallbackFonts = FallbackFonts;
#endif
}
void Graphics::DisposeDevice()

View File

@@ -911,10 +911,10 @@ bool ModelData::Pack2AnimationHeader(WriteStream* stream, int32 animIndex) const
}
// Info
stream->WriteInt32(100); // Header version (for fast version upgrades without serialization format change)
stream->WriteInt32(103); // Header version (for fast version upgrades without serialization format change)
stream->WriteDouble(anim.Duration);
stream->WriteDouble(anim.FramesPerSecond);
stream->WriteBool(anim.EnableRootMotion);
stream->WriteByte((byte)anim.RootMotionFlags);
stream->WriteString(anim.RootNodeName, 13);
// Animation channels
@@ -928,6 +928,12 @@ bool ModelData::Pack2AnimationHeader(WriteStream* stream, int32 animIndex) const
Serialization::Serialize(*stream, channel.Scale);
}
// Animation events
stream->WriteInt32(0);
// Nested animations
stream->WriteInt32(0);
return false;
}

View File

@@ -91,7 +91,7 @@ public:
FORCE_INLINE SkeletonNode& RootNode()
{
ASSERT(Nodes.HasItems());
return Nodes[0];
return Nodes.Get()[0];
}
/// <summary>
@@ -100,52 +100,24 @@ public:
FORCE_INLINE const SkeletonNode& RootNode() const
{
ASSERT(Nodes.HasItems());
return Nodes[0];
return Nodes.Get()[0];
}
/// <summary>
/// Swaps the contents of object with the other object without copy operation. Performs fast internal data exchange.
/// </summary>
void Swap(SkeletonData& other)
{
Nodes.Swap(other.Nodes);
Bones.Swap(other.Bones);
}
void Swap(SkeletonData& other);
int32 FindNode(const StringView& name) const
{
for (int32 i = 0; i < Nodes.Count(); i++)
{
if (Nodes[i].Name == name)
return i;
}
return -1;
}
Transform GetNodeTransform(int32 nodeIndex) const;
void SetNodeTransform(int32 nodeIndex, const Transform& value);
int32 FindBone(int32 nodeIndex) const
{
for (int32 i = 0; i < Bones.Count(); i++)
{
if (Bones[i].NodeIndex == nodeIndex)
return i;
}
return -1;
}
int32 FindNode(const StringView& name) const;
int32 FindBone(int32 nodeIndex) const;
uint64 GetMemoryUsage() const
{
uint64 result = Nodes.Capacity() * sizeof(SkeletonNode) + Bones.Capacity() * sizeof(SkeletonBone);
for (const auto& e : Nodes)
result += (e.Name.Length() + 1) * sizeof(Char);
return result;
}
uint64 GetMemoryUsage() const;
/// <summary>
/// Releases data.
/// </summary>
void Dispose()
{
Nodes.Resize(0);
Bones.Resize(0);
}
void Dispose();
};

View File

@@ -18,6 +18,69 @@
#include "Engine/Threading/Task.h"
#include "Engine/Threading/Threading.h"
void SkeletonData::Swap(SkeletonData& other)
{
Nodes.Swap(other.Nodes);
Bones.Swap(other.Bones);
}
Transform SkeletonData::GetNodeTransform(int32 nodeIndex) const
{
const int32 parentIndex = Nodes[nodeIndex].ParentIndex;
if (parentIndex == -1)
{
return Nodes[nodeIndex].LocalTransform;
}
const Transform parentTransform = GetNodeTransform(parentIndex);
return parentTransform.LocalToWorld(Nodes[nodeIndex].LocalTransform);
}
void SkeletonData::SetNodeTransform(int32 nodeIndex, const Transform& value)
{
const int32 parentIndex = Nodes[nodeIndex].ParentIndex;
if (parentIndex == -1)
{
Nodes[nodeIndex].LocalTransform = value;
return;
}
const Transform parentTransform = GetNodeTransform(parentIndex);
parentTransform.WorldToLocal(value, Nodes[nodeIndex].LocalTransform);
}
int32 SkeletonData::FindNode(const StringView& name) const
{
for (int32 i = 0; i < Nodes.Count(); i++)
{
if (Nodes[i].Name == name)
return i;
}
return -1;
}
int32 SkeletonData::FindBone(int32 nodeIndex) const
{
for (int32 i = 0; i < Bones.Count(); i++)
{
if (Bones[i].NodeIndex == nodeIndex)
return i;
}
return -1;
}
uint64 SkeletonData::GetMemoryUsage() const
{
uint64 result = Nodes.Capacity() * sizeof(SkeletonNode) + Bones.Capacity() * sizeof(SkeletonBone);
for (const auto& e : Nodes)
result += (e.Name.Length() + 1) * sizeof(Char);
return result;
}
void SkeletonData::Dispose()
{
Nodes.Resize(0);
Bones.Resize(0);
}
void SkinnedMesh::Init(SkinnedModel* model, int32 lodIndex, int32 index, int32 materialSlotIndex, const BoundingBox& box, const BoundingSphere& sphere)
{
_model = model;

View File

@@ -27,7 +27,7 @@ namespace FlaxEngine
Matrix.Invert(ref View, out IV);
Matrix.Invert(ref Projection, out IP);
Matrix.Multiply(ref View, ref Projection, out var viewProjection);
Frustum = new BoundingFrustum(viewProjection);
Frustum = new BoundingFrustum(ref viewProjection);
Matrix.Invert(ref viewProjection, out IVP);
CullingFrustum = Frustum;
}

View File

@@ -15,6 +15,7 @@
#include "Engine/Graphics/GPULimits.h"
#include "Engine/Threading/ThreadPoolTask.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Enums.h"
namespace
@@ -158,29 +159,6 @@ bool GPUTextureDescription::Equals(const GPUTextureDescription& other) const
String GPUTextureDescription::ToString() const
{
// TODO: add tool to Format to string
String flags;
if (Flags == GPUTextureFlags::None)
{
flags = TEXT("None");
}
else
{
// TODO: create tool to auto convert flag enums to string
#define CONVERT_FLAGS_FLAGS_2_STR(value) if (EnumHasAnyFlags(Flags, GPUTextureFlags::value)) { if (flags.HasChars()) flags += TEXT('|'); flags += TEXT(#value); }
CONVERT_FLAGS_FLAGS_2_STR(ShaderResource);
CONVERT_FLAGS_FLAGS_2_STR(RenderTarget);
CONVERT_FLAGS_FLAGS_2_STR(UnorderedAccess);
CONVERT_FLAGS_FLAGS_2_STR(DepthStencil);
CONVERT_FLAGS_FLAGS_2_STR(PerMipViews);
CONVERT_FLAGS_FLAGS_2_STR(PerSliceViews);
CONVERT_FLAGS_FLAGS_2_STR(ReadOnlyDepthView);
CONVERT_FLAGS_FLAGS_2_STR(BackBuffer);
#undef CONVERT_FLAGS_FLAGS_2_STR
}
return String::Format(TEXT("Size: {0}x{1}x{2}[{3}], Type: {4}, Mips: {5}, Format: {6}, MSAA: {7}, Flags: {8}, Usage: {9}"),
Width,
Height,
@@ -190,7 +168,7 @@ String GPUTextureDescription::ToString() const
MipLevels,
ScriptingEnum::ToString(Format),
::ToString(MultiSampleLevel),
flags,
ScriptingEnum::ToStringFlags(Flags),
(int32)Usage);
}
@@ -545,7 +523,7 @@ GPUTexture* GPUTexture::ToStagingUpload() const
bool GPUTexture::Resize(int32 width, int32 height, int32 depth, PixelFormat format)
{
// Validate texture is created
PROFILE_CPU();
if (!IsAllocated())
{
LOG(Warning, "Cannot resize not created textures.");
@@ -609,6 +587,7 @@ GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipInde
GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, int32 rowPitch, int32 slicePitch, bool copyData)
{
PROFILE_CPU();
ASSERT(IsAllocated());
ASSERT(mipIndex < MipLevels() && data.IsValid());
ASSERT(data.Length() >= slicePitch);
@@ -700,6 +679,7 @@ bool GPUTexture::DownloadData(TextureData& result)
{
MISSING_CODE("support volume texture data downloading.");
}
PROFILE_CPU();
// Use faster path for staging resources
if (IsStaging())
@@ -781,6 +761,7 @@ Task* GPUTexture::DownloadDataAsync(TextureData& result)
{
MISSING_CODE("support volume texture data downloading.");
}
PROFILE_CPU();
// Use faster path for staging resources
if (IsStaging())

View File

@@ -1356,6 +1356,16 @@ bool Actor::IsPrefabRoot() const
return _isPrefabRoot != 0;
}
Actor* Actor::GetPrefabRoot()
{
if (!HasPrefabLink())
return nullptr;
Actor* result = this;
while (result && !result->IsPrefabRoot())
result = result->GetParent();
return result;
}
Actor* Actor::FindActor(const StringView& name) const
{
Actor* result = nullptr;
@@ -1376,14 +1386,16 @@ Actor* Actor::FindActor(const StringView& name) const
return result;
}
Actor* Actor::FindActor(const MClass* type) const
Actor* Actor::FindActor(const MClass* type, bool activeOnly) const
{
CHECK_RETURN(type, nullptr);
if (activeOnly && !_isActive)
return nullptr;
if (GetClass()->IsSubClassOf(type))
return const_cast<Actor*>(this);
for (auto child : Children)
{
const auto actor = child->FindActor(type);
const auto actor = child->FindActor(type, activeOnly);
if (actor)
return actor;
}
@@ -1404,14 +1416,16 @@ Actor* Actor::FindActor(const MClass* type, const StringView& name) const
return nullptr;
}
Actor* Actor::FindActor(const MClass* type, const Tag& tag) const
Actor* Actor::FindActor(const MClass* type, const Tag& tag, bool activeOnly) const
{
CHECK_RETURN(type, nullptr);
if (activeOnly && !_isActive)
return nullptr;
if (GetClass()->IsSubClassOf(type) && HasTag(tag))
return const_cast<Actor*>(this);
for (auto child : Children)
{
const auto actor = child->FindActor(type, tag);
const auto actor = child->FindActor(type, tag, activeOnly);
if (actor)
return actor;
}

View File

@@ -253,10 +253,11 @@ namespace FlaxEngine
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>
/// <typeparam name="T">Type of the object.</typeparam>
/// <param name="activeOnly">Finds only a active actor.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
public T FindActor<T>() where T : Actor
public T FindActor<T>(bool activeOnly = false) where T : Actor
{
return FindActor(typeof(T)) as T;
return FindActor(typeof(T), activeOnly) as T;
}
/// <summary>
@@ -269,16 +270,17 @@ namespace FlaxEngine
{
return FindActor(typeof(T), name) as T;
}
/// <summary>
/// Tries to find actor of the given type and tag in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>
/// <param name="tag">A tag on the object.</param>
/// <typeparam name="T">Type of the object.</typeparam>
/// <param name="activeOnly">Finds only an active actor.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
public T FindActor<T>(Tag tag) where T : Actor
public T FindActor<T>(Tag tag, bool activeOnly = false) where T : Actor
{
return FindActor(typeof(T), tag) as T;
return FindActor(typeof(T), tag, activeOnly) as T;
}
/// <summary>
@@ -386,5 +388,9 @@ namespace FlaxEngine
{
return $"{Name} ({GetType().Name})";
}
#if FLAX_EDITOR
internal bool ShowTransform => !(this is UIControl);
#endif
}
}

View File

@@ -534,9 +534,7 @@ public:
/// <summary>
/// Gets actor direction vector (forward vector).
/// </summary>
/// <returns>The result value.</returns>
API_PROPERTY(Attributes="HideInEditor, NoSerialize")
FORCE_INLINE Float3 GetDirection() const
API_PROPERTY(Attributes="HideInEditor, NoSerialize") FORCE_INLINE Float3 GetDirection() const
{
return Float3::Transform(Float3::Forward, GetOrientation());
}
@@ -571,7 +569,7 @@ public:
/// <summary>
/// Gets local position of the actor in parent actor space.
/// </summary>
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Position\"), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorOrder(-30), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+PositionEditor\")")
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Position\"), VisibleIf(\"ShowTransform\"), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorOrder(-30), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+PositionEditor\")")
FORCE_INLINE Vector3 GetLocalPosition() const
{
return _localTransform.Translation;
@@ -587,7 +585,7 @@ public:
/// Gets local rotation of the actor in parent actor space.
/// </summary>
/// <code>Actor.LocalOrientation *= Quaternion.Euler(0, 10 * Time.DeltaTime, 0)</code>
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Rotation\"), DefaultValue(typeof(Quaternion), \"0,0,0,1\"), EditorOrder(-20), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+OrientationEditor\")")
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Rotation\"), VisibleIf(\"ShowTransform\"), DefaultValue(typeof(Quaternion), \"0,0,0,1\"), EditorOrder(-20), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+OrientationEditor\")")
FORCE_INLINE Quaternion GetLocalOrientation() const
{
return _localTransform.Orientation;
@@ -602,7 +600,7 @@ public:
/// <summary>
/// Gets local scale vector of the actor in parent actor space.
/// </summary>
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Scale\"), DefaultValue(typeof(Float3), \"1,1,1\"), Limit(float.MinValue, float.MaxValue, 0.01f), EditorOrder(-10), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+ScaleEditor\")")
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Scale\"), VisibleIf(\"ShowTransform\"), DefaultValue(typeof(Float3), \"1,1,1\"), Limit(float.MinValue, float.MaxValue, 0.01f), EditorOrder(-10), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+ScaleEditor\")")
FORCE_INLINE Float3 GetLocalScale() const
{
return _localTransform.Scale;
@@ -739,6 +737,12 @@ public:
/// </summary>
API_PROPERTY() bool IsPrefabRoot() const;
/// <summary>
/// Gets the root of the prefab this actor is attached to.
/// </summary>
/// <returns>The root prefab object, or null if this actor is not a prefab.</returns>
API_FUNCTION() Actor* GetPrefabRoot();
public:
/// <summary>
/// Tries to find the actor with the given name in this actor hierarchy (checks this actor and all children hierarchy).
@@ -751,8 +755,9 @@ public:
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <param name="activeOnly">Finds only a active actor.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const;
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, bool activeOnly = false) const;
/// <summary>
/// Tries to find the actor of the given type and name in this actor hierarchy (checks this actor and all children hierarchy).
@@ -767,8 +772,9 @@ public:
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <param name="tag">The tag of the actor to search for.</param>
/// <param name="activeOnly">Finds only an active actor.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag) const;
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag, bool activeOnly = false) const;
/// <summary>
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
@@ -790,7 +796,7 @@ public:
{
return (T*)FindActor(T::GetStaticClass(), name);
}
/// <summary>
/// Tries to find the actor of the given type and tag in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>

View File

@@ -1393,13 +1393,13 @@ Actor* Level::FindActor(const StringView& name)
return result;
}
Actor* Level::FindActor(const MClass* type)
Actor* Level::FindActor(const MClass* type, bool activeOnly)
{
CHECK_RETURN(type, nullptr);
Actor* result = nullptr;
ScopeLock lock(ScenesLock);
for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++)
result = Scenes[i]->FindActor(type);
result = Scenes[i]->FindActor(type, activeOnly);
return result;
}
@@ -1413,29 +1413,33 @@ Actor* Level::FindActor(const MClass* type, const StringView& name)
return result;
}
Actor* FindActorRecursive(Actor* node, const Tag& tag)
Actor* FindActorRecursive(Actor* node, const Tag& tag, bool activeOnly)
{
if (activeOnly && !node->GetIsActive())
return nullptr;
if (node->HasTag(tag))
return node;
Actor* result = nullptr;
for (Actor* child : node->Children)
{
result = FindActorRecursive(child, tag);
result = FindActorRecursive(child, tag, activeOnly);
if (result)
break;
}
return result;
}
Actor* FindActorRecursiveByType(Actor* node, const MClass* type, const Tag& tag)
Actor* FindActorRecursiveByType(Actor* node, const MClass* type, const Tag& tag, bool activeOnly)
{
CHECK_RETURN(type, nullptr);
if (activeOnly && !node->GetIsActive())
return nullptr;
if (node->HasTag(tag) && node->GetClass()->IsSubClassOf(type))
return node;
Actor* result = nullptr;
for (Actor* child : node->Children)
{
result = FindActorRecursiveByType(child, type, tag);
result = FindActorRecursiveByType(child, type, tag, activeOnly);
if (result)
break;
}
@@ -1468,30 +1472,30 @@ void FindActorsRecursiveByParentTags(Actor* node, const Array<Tag>& tags, const
FindActorsRecursiveByParentTags(child, tags, activeOnly, result);
}
Actor* Level::FindActor(const Tag& tag, Actor* root)
Actor* Level::FindActor(const Tag& tag, bool activeOnly, Actor* root)
{
PROFILE_CPU();
if (root)
return FindActorRecursive(root, tag);
return FindActorRecursive(root, tag, activeOnly);
Actor* result = nullptr;
for (Scene* scene : Scenes)
{
result = FindActorRecursive(scene, tag);
result = FindActorRecursive(scene, tag, activeOnly);
if (result)
break;
}
return result;
}
Actor* Level::FindActor(const MClass* type, const Tag& tag, Actor* root)
Actor* Level::FindActor(const MClass* type, const Tag& tag, bool activeOnly, Actor* root)
{
CHECK_RETURN(type, nullptr);
if (root)
return FindActorRecursiveByType(root, type, tag);
return FindActorRecursiveByType(root, type, tag, activeOnly);
Actor* result = nullptr;
ScopeLock lock(ScenesLock);
for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++)
result = Scenes[i]->FindActor(type, tag);
result = Scenes[i]->FindActor(type, tag, activeOnly);
return result;
}
@@ -1562,12 +1566,14 @@ Script* Level::FindScript(const MClass* type)
namespace
{
void GetActors(const MClass* type, Actor* actor, Array<Actor*>& result)
void GetActors(const MClass* type, Actor* actor, bool activeOnly, Array<Actor*>& result)
{
if (activeOnly && !actor->GetIsActive())
return;
if (actor->GetClass()->IsSubClassOf(type))
result.Add(actor);
for (auto child : actor->Children)
GetActors(type, child, result);
GetActors(type, child, activeOnly, result);
}
void GetScripts(const MClass* type, Actor* actor, Array<Script*>& result)
@@ -1580,13 +1586,13 @@ namespace
}
}
Array<Actor*> Level::GetActors(const MClass* type)
Array<Actor*> Level::GetActors(const MClass* type, bool activeOnly)
{
Array<Actor*> result;
CHECK_RETURN(type, result);
ScopeLock lock(ScenesLock);
for (int32 i = 0; i < Scenes.Count(); i++)
::GetActors(type, Scenes[i], result);
::GetActors(type, Scenes[i], activeOnly, result);
return result;
}

View File

@@ -109,6 +109,8 @@ namespace FlaxEngine
public static T[] GetScripts<T>() where T : Script
{
var scripts = GetScripts(typeof(T));
if (scripts.Length == 0)
return Array.Empty<T>();
var result = new T[scripts.Length];
for (int i = 0; i < scripts.Length; i++)
result[i] = scripts[i] as T;
@@ -119,10 +121,13 @@ namespace FlaxEngine
/// Finds all the actors of the given type in all the loaded scenes.
/// </summary>
/// <typeparam name="T">Type of the object.</typeparam>
/// <param name="activeOnly">Finds only active actors.</param>
/// <returns>Found actors list.</returns>
public static T[] GetActors<T>() where T : Actor
public static T[] GetActors<T>(bool activeOnly = false) where T : Actor
{
var actors = GetActors(typeof(T));
var actors = GetActors(typeof(T), activeOnly);
if (actors.Length == 0)
return Array.Empty<T>();
var result = new T[actors.Length];
for (int i = 0; i < actors.Length; i++)
result[i] = actors[i] as T;

View File

@@ -360,8 +360,9 @@ public:
/// Tries to find the actor of the given type in all the loaded scenes.
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <param name="activeOnly">Finds only an active actor.</param>
/// <returns>Found actor or null.</returns>
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type);
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, bool activeOnly = false);
/// <summary>
/// Tries to find the actor of the given type and name in all the loaded scenes.
@@ -375,18 +376,20 @@ public:
/// Tries to find the actor with the given tag (returns the first one found).
/// </summary>
/// <param name="tag">The tag of the actor to search for.</param>
/// <param name="activeOnly">Finds only an active actor.</param>
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
/// <returns>Found actor or null.</returns>
API_FUNCTION() static Actor* FindActor(const Tag& tag, Actor* root = nullptr);
API_FUNCTION() static Actor* FindActor(const Tag& tag, bool activeOnly = false, Actor* root = nullptr);
/// <summary>
/// Tries to find the actor of the given type and tag in all the loaded scenes.
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <param name="tag">The tag of the actor to search for.</param>
/// <param name="activeOnly">Finds only an active actor.</param>
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag, Actor* root = nullptr);
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag, bool activeOnly = false, Actor* root = nullptr);
/// <summary>
/// Tries to find the actors with the given tag (returns all found).
@@ -460,8 +463,9 @@ public:
/// Finds all the actors of the given type in all the loaded scenes.
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <param name="activeOnly">Finds only active actors in the scene.</param>
/// <returns>Found actors list.</returns>
API_FUNCTION() static Array<Actor*> GetActors(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type);
API_FUNCTION() static Array<Actor*> GetActors(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, bool activeOnly = false);
/// <summary>
/// Finds all the scripts of the given type in all the loaded scenes.

View File

@@ -63,7 +63,7 @@ SceneObjectsFactory::Context::~Context()
{
if (Async)
{
Array<ISerializeModifier*, FixedAllocation<PLATFORM_THREADS_LIMIT>> modifiers;
Array<ISerializeModifier*, InlinedAllocation<PLATFORM_THREADS_LIMIT>> modifiers;
Modifiers.GetValues(modifiers);
for (ISerializeModifier* e : modifiers)
{
@@ -475,7 +475,7 @@ void SceneObjectsFactory::SetupPrefabInstances(Context& context, const PrefabSyn
const ISerializable::DeserializeStream* prefabData;
if (prefab->ObjectsDataCache.TryGet(prefabObjectId, prefabData) && JsonTools::GetGuidIfValid(prefabObjectId, *prefabData, "PrefabObjectID"))
{
prefabId = JsonTools::GetGuid(stream, "PrefabID");
prefabId = JsonTools::GetGuid(*prefabData, "PrefabID");
prefab = Content::LoadAsync<Prefab>(prefabId);
if (prefab && !prefab->WaitForLoaded())
{

View File

@@ -714,6 +714,7 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b
stream->SenderId = senderClientId;
// Deserialize object
Scripting::ObjectsLookupIdMapping.Set(&IdsRemappingTable);
const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, false);
if (failed)
{

View File

@@ -52,7 +52,7 @@ namespace
int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index)
{
const auto node = _graph.SpawnModules[index];
auto& context = Context.Get();
auto& context = *Context.Get();
auto& data = context.Data->SpawnModulesData[index];
// Accumulate the previous frame fraction
@@ -120,7 +120,7 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index)
void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* node, int32 particlesStart, int32 particlesEnd)
{
auto& context = Context.Get();
auto& context = *Context.Get();
auto stride = context.Data->Buffer->Stride;
auto start = context.Data->Buffer->GetParticleCPU(particlesStart);

View File

@@ -12,7 +12,7 @@
void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value)
{
auto& context = Context.Get();
auto& context = *Context.Get();
switch (node->TypeID)
{
// Get
@@ -168,7 +168,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTextures(Box* box, Node* node,
void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
{
auto& context = Context.Get();
auto& context = *Context.Get();
switch (node->TypeID)
{
// Linearize Depth
@@ -202,7 +202,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va
void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* nodeBase, Value& value)
{
auto& context = Context.Get();
auto& context = *Context.Get();
auto node = (ParticleEmitterGraphCPUNode*)nodeBase;
switch (node->TypeID)
{
@@ -468,7 +468,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node
void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, Value& value)
{
auto& context = Context.Get();
auto& context = *Context.Get();
switch (node->TypeID)
{
// Function Input

View File

@@ -8,7 +8,7 @@
#include "Engine/Engine/Time.h"
#include "Engine/Profiler/ProfilerCPU.h"
ThreadLocal<ParticleEmitterGraphCPUContext> ParticleEmitterGraphCPUExecutor::Context;
ThreadLocal<ParticleEmitterGraphCPUContext*> ParticleEmitterGraphCPUExecutor::Context;
namespace
{
@@ -122,7 +122,10 @@ ParticleEmitterGraphCPUExecutor::ParticleEmitterGraphCPUExecutor(ParticleEmitter
void ParticleEmitterGraphCPUExecutor::Init(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt)
{
auto& context = Context.Get();
auto& contextPtr = Context.Get();
if (!contextPtr)
contextPtr = New<ParticleEmitterGraphCPUContext>();
auto& context = *contextPtr;
context.GraphStack.Clear();
context.GraphStack.Push(&_graph);
context.Data = &data;
@@ -252,8 +255,8 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa
case 401:
{
// Prepare graph data
auto& context = Context.Get();
Init(emitter, effect, data);
auto& context = *Context.Get();
// Find the maximum radius of the particle light
float maxRadius = 0.0f;
@@ -377,7 +380,7 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff
// Prepare graph data
Init(emitter, effect, data);
auto& context = Context.Get();
auto& context = *Context.Get();
// Draw lights
for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.LightModules.Count(); moduleIndex++)
@@ -571,7 +574,6 @@ int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, Par
PROFILE_CPU_NAMED("Spawn");
// Prepare data
auto& context = Context.Get();
Init(emitter, effect, data, dt);
// Spawn particles
@@ -587,7 +589,7 @@ int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, Par
VisjectExecutor::Value ParticleEmitterGraphCPUExecutor::eatBox(Node* caller, Box* box)
{
// Check if graph is looped or is too deep
auto& context = Context.Get();
auto& context = *Context.Get();
if (context.CallStackSize >= PARTICLE_EMITTER_MAX_CALL_STACK)
{
OnError(caller, box, TEXT("Graph is looped or too deep!"));
@@ -618,6 +620,6 @@ VisjectExecutor::Value ParticleEmitterGraphCPUExecutor::eatBox(Node* caller, Box
VisjectExecutor::Graph* ParticleEmitterGraphCPUExecutor::GetCurrentGraph() const
{
auto& context = Context.Get();
auto& context = *Context.Get();
return (Graph*)context.GraphStack.Peek();
}

View File

@@ -133,7 +133,7 @@ private:
ParticleEmitterGraphCPU& _graph;
// Per-thread context to allow async execution
static ThreadLocal<ParticleEmitterGraphCPUContext> Context;
static ThreadLocal<ParticleEmitterGraphCPUContext*> Context;
public:
/// <summary>

View File

@@ -415,9 +415,9 @@ void Cloth::OnDebugDrawSelected()
c1 = Color::Lerp(Color::Red, Color::White, _paint[i1]);
c2 = Color::Lerp(Color::Red, Color::White, _paint[i2]);
}
DebugDraw::DrawLine(v0, v1, c0, c1, 0, false);
DebugDraw::DrawLine(v1, v2, c1, c2, 0, false);
DebugDraw::DrawLine(v2, v0, c2, c0, 0, false);
DebugDraw::DrawLine(v0, v1, c0, c1, 0, DebugDrawDepthTest);
DebugDraw::DrawLine(v1, v2, c1, c2, 0, DebugDrawDepthTest);
DebugDraw::DrawLine(v2, v0, c2, c0, 0, DebugDrawDepthTest);
}
PhysicsBackend::UnlockClothParticles(_cloth);
}

View File

@@ -332,6 +332,11 @@ public:
bool OnPreUpdate();
void OnPostUpdate();
private:
#if USE_EDITOR
API_FIELD(Internal) bool DebugDrawDepthTest = true;
#endif
public:
// [Actor]
void Draw(RenderContext& renderContext) override;

View File

@@ -5,6 +5,7 @@
#include "Engine/Level/Actor.h"
#include "Engine/Physics/Collisions.h"
struct RayCastHit;
struct Collision;
/// <summary>
@@ -42,6 +43,40 @@ public:
/// <returns>The rigid body or null.</returns>
API_PROPERTY() virtual RigidBody* GetAttachedRigidBody() const = 0;
/// <summary>
/// Performs a raycast against this collider shape.
/// </summary>
/// <param name="origin">The origin of the ray.</param>
/// <param name="direction">The normalized direction of the ray.</param>
/// <param name="resultHitDistance">The raycast result hit position distance from the ray origin. Valid only if raycast hits anything.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
API_FUNCTION(Sealed) virtual bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) float& resultHitDistance, float maxDistance = MAX_float) const = 0;
/// <summary>
/// Performs a raycast against this collider, returns results in a RaycastHit structure.
/// </summary>
/// <param name="origin">The origin of the ray.</param>
/// <param name="direction">The normalized direction of the ray.</param>
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
API_FUNCTION(Sealed) virtual bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, float maxDistance = MAX_float) const = 0;
/// <summary>
/// Gets a point on the collider that is closest to a given location. Can be used to find a hit location or position to apply explosion force or any other special effects.
/// </summary>
/// <param name="point">The position to find the closest point to it.</param>
/// <param name="result">The result point on the collider that is closest to the specified location.</param>
API_FUNCTION(Sealed) virtual void ClosestPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const = 0;
/// <summary>
/// Checks if a point is inside the collider.
/// </summary>
/// <param name="point">The point to check if is contained by the collider shape (in world-space).</param>
/// <returns>True if collider shape contains a given point, otherwise false.</returns>
API_FUNCTION(Sealed) virtual bool ContainsPoint(const Vector3& point) const = 0;
public:
/// <summary>
/// Called when a collision start gets registered for this collider (it collides with something).

View File

@@ -206,11 +206,11 @@ void CharacterController::CreateController()
_cachedScale = GetScale();
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const Vector3 position = _transform.LocalToWorld(_center);
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material.Get(), Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape);
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape);
// Setup
PhysicsBackend::SetControllerUpDirection(_controller, _upDirection);
PhysicsBackend::SetShapeLocalPose(_shape, _center, Quaternion::Identity);
PhysicsBackend::SetShapeLocalPose(_shape, Vector3::Zero, Quaternion::Identity);
UpdateLayerBits();
UpdateBounds();
}
@@ -280,12 +280,8 @@ void CharacterController::OnActiveTransformChanged()
// Change actor transform (but with locking)
ASSERT(!_isUpdatingTransform);
_isUpdatingTransform = true;
Transform transform;
PhysicsBackend::GetRigidActorPose(PhysicsBackend::GetShapeActor(_shape), transform.Translation, transform.Orientation);
transform.Translation -= _center;
transform.Orientation = _transform.Orientation;
transform.Scale = _transform.Scale;
SetTransform(transform);
const Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center;
SetPosition(position);
_isUpdatingTransform = false;
UpdateBounds();

View File

@@ -201,7 +201,7 @@ void Collider::CreateShape()
// Create shape
const bool isTrigger = _isTrigger && CanBeTrigger();
_shape = PhysicsBackend::CreateShape(this, shape, Material.Get(), IsActiveInHierarchy(), isTrigger);
_shape = PhysicsBackend::CreateShape(this, shape, Material, IsActiveInHierarchy(), isTrigger);
PhysicsBackend::SetShapeContactOffset(_shape, _contactOffset);
UpdateLayerBits();
}
@@ -288,7 +288,7 @@ void Collider::OnMaterialChanged()
{
// Update the shape material
if (_shape)
PhysicsBackend::SetShapeMaterial(_shape, Material.Get());
PhysicsBackend::SetShapeMaterial(_shape, Material);
}
void Collider::BeginPlay(SceneBeginData* data)

View File

@@ -4,7 +4,7 @@
#include "Engine/Physics/Types.h"
#include "Engine/Content/JsonAsset.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Content/JsonAssetReference.h"
#include "Engine/Physics/Actors/PhysicsColliderActor.h"
struct RayCastHit;
@@ -80,44 +80,10 @@ public:
/// <summary>
/// The physical material used to define the collider physical properties.
/// </summary>
API_FIELD(Attributes="EditorOrder(2), DefaultValue(null), AssetReference(typeof(PhysicalMaterial), true), EditorDisplay(\"Collider\")")
AssetReference<JsonAsset> Material;
API_FIELD(Attributes="EditorOrder(2), DefaultValue(null), EditorDisplay(\"Collider\")")
JsonAssetReference<PhysicalMaterial> Material;
public:
/// <summary>
/// Performs a raycast against this collider shape.
/// </summary>
/// <param name="origin">The origin of the ray.</param>
/// <param name="direction">The normalized direction of the ray.</param>
/// <param name="resultHitDistance">The raycast result hit position distance from the ray origin. Valid only if raycast hits anything.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) float& resultHitDistance, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against this collider, returns results in a RaycastHit structure.
/// </summary>
/// <param name="origin">The origin of the ray.</param>
/// <param name="direction">The normalized direction of the ray.</param>
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, float maxDistance = MAX_float) const;
/// <summary>
/// Gets a point on the collider that is closest to a given location. Can be used to find a hit location or position to apply explosion force or any other special effects.
/// </summary>
/// <param name="point">The position to find the closest point to it.</param>
/// <param name="result">The result point on the collider that is closest to the specified location.</param>
API_FUNCTION() void ClosestPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const;
/// <summary>
/// Checks if a point is inside the collider.
/// </summary>
/// <param name="point">The point to check if is contained by the collider shape (in world-space).</param>
/// <returns>True if collider shape contains a given point, otherwise false.</returns>
API_FUNCTION() bool ContainsPoint(const Vector3& point) const;
/// <summary>
/// Computes minimum translational distance between two geometry objects.
/// Translating the first collider by direction * distance will separate the colliders apart if the function returned true. Otherwise, direction and distance are not defined.
@@ -198,6 +164,10 @@ private:
public:
// [PhysicsColliderActor]
RigidBody* GetAttachedRigidBody() const override;
bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance = MAX_float) const final;
bool RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance = MAX_float) const final;
void ClosestPoint(const Vector3& point, Vector3& result) const final;
bool ContainsPoint(const Vector3& point) const final;
protected:
// [PhysicsColliderActor]

View File

@@ -9,7 +9,7 @@ class PhysicsColliderActor;
/// <summary>
/// Contains a contact point data for the collision location.
/// </summary>
API_STRUCT() struct FLAXENGINE_API ContactPoint
API_STRUCT(NoDefault) struct FLAXENGINE_API ContactPoint
{
DECLARE_SCRIPTING_TYPE_MINIMAL(ContactPoint);
@@ -41,7 +41,7 @@ struct TIsPODType<ContactPoint>
/// <summary>
/// Contains a collision information passed to the OnCollisionEnter/OnCollisionExit events.
/// </summary>
API_STRUCT() struct FLAXENGINE_API Collision
API_STRUCT(NoDefault) struct FLAXENGINE_API Collision
{
DECLARE_SCRIPTING_TYPE_MINIMAL(Collision);
@@ -58,9 +58,7 @@ API_STRUCT() struct FLAXENGINE_API Collision
/// <summary>
/// The total impulse applied to this contact pair to resolve the collision.
/// </summary>
/// <remarks>
/// The total impulse is obtained by summing up impulses applied at all contact points in this collision pair.
/// </remarks>
/// <remarks>The total impulse is obtained by summing up impulses applied at all contact points in this collision pair.</remarks>
API_FIELD() Vector3 Impulse;
/// <summary>
@@ -81,15 +79,13 @@ API_STRUCT() struct FLAXENGINE_API Collision
/// <summary>
/// The contacts locations.
/// </summary>
API_FIELD(Private, NoArray) ContactPoint Contacts[COLLISION_NAX_CONTACT_POINTS];
API_FIELD(Internal, NoArray) ContactPoint Contacts[COLLISION_NAX_CONTACT_POINTS];
public:
/// <summary>
/// Gets the relative linear velocity of the two colliding objects.
/// </summary>
/// <remarks>
/// Can be used to detect stronger collisions.
/// </remarks>
/// <remarks>Can be used to detect stronger collisions. </remarks>
Vector3 GetRelativeVelocity() const
{
return ThisVelocity - OtherVelocity;

View File

@@ -159,7 +159,8 @@ void D6Joint::OnDebugDrawSelected()
const float twistSize = 9.0f;
const Color swingColor = Color::Green.AlphaMultiplied(0.6f);
const Color twistColor = Color::Yellow.AlphaMultiplied(0.5f);
DEBUG_DRAW_WIRE_ARROW(target, targetRotation, swingSize / 100.0f * 0.5f, Color::Red, 0, false);
const float arrowSize = swingSize / 100.0f * 0.5f;
DEBUG_DRAW_WIRE_ARROW(target, targetRotation, arrowSize, arrowSize * 0.5f, Color::Red, 0, false);
if (_motion[(int32)D6JointAxis::SwingY] == D6JointMotion::Locked && _motion[(int32)D6JointAxis::SwingZ] == D6JointMotion::Locked)
{
// Swing is locked

View File

@@ -63,8 +63,9 @@ void HingeJoint::OnDebugDrawSelected()
const Quaternion targetRotation = GetTargetOrientation() * xRotation;
const float size = 15.0f;
const Color color = Color::Green.AlphaMultiplied(0.6f);
DEBUG_DRAW_WIRE_ARROW(source, sourceRotation, size / 100.0f * 0.5f, Color::Red, 0, false);
DEBUG_DRAW_WIRE_ARROW(target, targetRotation, size / 100.0f * 0.5f, Color::Blue, 0, false);
const float arrowSize = size / 100.0f * 0.5f;
DEBUG_DRAW_WIRE_ARROW(source, sourceRotation, arrowSize, arrowSize * 0.5f, Color::Red, 0, false);
DEBUG_DRAW_WIRE_ARROW(target, targetRotation, arrowSize, arrowSize * 0.5f, Color::Blue, 0, false);
if (EnumHasAnyFlags(_flags, HingeJointFlag::Limit))
{
const float upper = Math::Max(_limit.Upper, _limit.Lower);

View File

@@ -38,8 +38,9 @@ void SphericalJoint::OnDebugDrawSelected()
const Vector3 source = GetPosition();
const Vector3 target = GetTargetPosition();
const float size = 15.0f;
const float arrowSize = size / 100.0f * 0.5f;
const Color color = Color::Green.AlphaMultiplied(0.6f);
DEBUG_DRAW_WIRE_ARROW(source, GetOrientation(), size / 100.0f * 0.5f, Color::Red, 0, false);
DEBUG_DRAW_WIRE_ARROW(source, GetOrientation(), arrowSize, arrowSize * 0.5f, Color::Red, 0, false);
if (EnumHasAnyFlags(_flags, SphericalJointFlag::Limit))
{
DEBUG_DRAW_CONE(source, GetOrientation(), size, _limit.YLimitAngle * DegreesToRadians, _limit.ZLimitAngle * DegreesToRadians, color, 0, false);

View File

@@ -228,9 +228,7 @@ class QueryFilterPhysX : public PxQueryFilterCallback
// Check mask
const PxFilterData shapeFilter = shape->getQueryFilterData();
if ((filterData.word0 & shapeFilter.word0) == 0)
{
return PxQueryHitType::eNONE;
}
// Check if skip triggers
const bool hitTriggers = filterData.word2 != 0;
@@ -483,8 +481,10 @@ protected:
}
};
#define PxHitFlagEmpty (PxHitFlags)0
#define SCENE_QUERY_FLAGS (PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX | PxHitFlag::eUV)
#define SCENE_QUERY_SETUP(blockSingle) auto scenePhysX = (ScenePhysX*)scene; if (scene == nullptr) return false; \
const PxHitFlags hitFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eUV; \
PxQueryFilterData filterData; \
filterData.flags |= PxQueryFlag::ePREFILTER; \
filterData.data.word0 = layerMask; \
@@ -644,6 +644,19 @@ void GetShapeGeometry(const CollisionShape& shape, PxGeometryHolder& geometry)
}
}
void GetShapeMaterials(Array<PxMaterial*, InlinedAllocation<1>>& materialsPhysX, Span<JsonAsset*> materials)
{
materialsPhysX.Resize(materials.Length());
for (int32 i = 0; i < materials.Length(); i++)
{
PxMaterial* materialPhysX = DefaultMaterial;
const JsonAsset* material = materials.Get()[i];
if (material && !material->WaitForLoaded() && material->Instance)
materialPhysX = (PxMaterial*)((PhysicalMaterial*)material->Instance)->GetPhysicsMaterial();
materialsPhysX.Get()[i] = materialPhysX;
}
}
PxFilterFlags FilterShader(
PxFilterObjectAttributes attributes0, PxFilterData filterData0,
PxFilterObjectAttributes attributes1, PxFilterData filterData1,
@@ -1735,8 +1748,6 @@ void PhysicsBackend::EndSimulateScene(void* scene)
{
PROFILE_CPU_NAMED("Physics.SendEvents");
scenePhysX->EventsCallback.CollectResults();
scenePhysX->EventsCallback.SendTriggerEvents();
scenePhysX->EventsCallback.SendCollisionEvents();
scenePhysX->EventsCallback.SendJointEvents();
@@ -1880,14 +1891,14 @@ bool PhysicsBackend::RayCast(void* scene, const Vector3& origin, const Vector3&
{
SCENE_QUERY_SETUP(true);
PxRaycastBuffer buffer;
return scenePhysX->Scene->raycast(C2P(origin - scenePhysX->Origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter);
return scenePhysX->Scene->raycast(C2P(origin - scenePhysX->Origin), C2P(direction), maxDistance, buffer, PxHitFlagEmpty, filterData, &QueryFilter);
}
bool PhysicsBackend::RayCast(void* scene, const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
SCENE_QUERY_SETUP(true);
PxRaycastBuffer buffer;
if (!scenePhysX->Scene->raycast(C2P(origin - scenePhysX->Origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->raycast(C2P(origin - scenePhysX->Origin), C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_SINGLE();
return true;
@@ -1897,7 +1908,7 @@ bool PhysicsBackend::RayCastAll(void* scene, const Vector3& origin, const Vector
{
SCENE_QUERY_SETUP(false);
DynamicHitBuffer<PxRaycastHit> buffer;
if (!scenePhysX->Scene->raycast(C2P(origin - scenePhysX->Origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->raycast(C2P(origin - scenePhysX->Origin), C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_ALL();
return true;
@@ -1908,7 +1919,7 @@ bool PhysicsBackend::BoxCast(void* scene, const Vector3& center, const Vector3&
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxBoxGeometry geometry(C2P(halfExtents));
return scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter);
return scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, PxHitFlagEmpty, filterData, &QueryFilter);
}
bool PhysicsBackend::BoxCast(void* scene, const Vector3& center, const Vector3& halfExtents, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
@@ -1916,7 +1927,7 @@ bool PhysicsBackend::BoxCast(void* scene, const Vector3& center, const Vector3&
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxBoxGeometry geometry(C2P(halfExtents));
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_SINGLE();
return true;
@@ -1927,7 +1938,7 @@ bool PhysicsBackend::BoxCastAll(void* scene, const Vector3& center, const Vector
SCENE_QUERY_SETUP_SWEEP();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxBoxGeometry geometry(C2P(halfExtents));
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_ALL();
return true;
@@ -1938,7 +1949,7 @@ bool PhysicsBackend::SphereCast(void* scene, const Vector3& center, const float
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin));
const PxSphereGeometry geometry(radius);
return scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter);
return scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, PxHitFlagEmpty, filterData, &QueryFilter);
}
bool PhysicsBackend::SphereCast(void* scene, const Vector3& center, const float radius, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
@@ -1946,7 +1957,7 @@ bool PhysicsBackend::SphereCast(void* scene, const Vector3& center, const float
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin));
const PxSphereGeometry geometry(radius);
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_SINGLE();
return true;
@@ -1957,7 +1968,7 @@ bool PhysicsBackend::SphereCastAll(void* scene, const Vector3& center, const flo
SCENE_QUERY_SETUP_SWEEP();
const PxTransform pose(C2P(center - scenePhysX->Origin));
const PxSphereGeometry geometry(radius);
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_ALL();
return true;
@@ -1968,7 +1979,7 @@ bool PhysicsBackend::CapsuleCast(void* scene, const Vector3& center, const float
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
return scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter);
return scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, PxHitFlagEmpty, filterData, &QueryFilter);
}
bool PhysicsBackend::CapsuleCast(void* scene, const Vector3& center, const float radius, const float height, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
@@ -1976,7 +1987,7 @@ bool PhysicsBackend::CapsuleCast(void* scene, const Vector3& center, const float
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_SINGLE();
return true;
@@ -1987,7 +1998,7 @@ bool PhysicsBackend::CapsuleCastAll(void* scene, const Vector3& center, const fl
SCENE_QUERY_SETUP_SWEEP();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_ALL();
return true;
@@ -1999,7 +2010,7 @@ bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const Collis
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
return scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter);
return scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, PxHitFlagEmpty, filterData, &QueryFilter);
}
bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
@@ -2008,7 +2019,7 @@ bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const Collis
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_SINGLE();
return true;
@@ -2020,7 +2031,7 @@ bool PhysicsBackend::ConvexCastAll(void* scene, const Vector3& center, const Col
SCENE_QUERY_SETUP_SWEEP();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, &QueryFilter))
if (!scenePhysX->Scene->sweep(geometry, pose, C2P(direction), maxDistance, buffer, SCENE_QUERY_FLAGS, filterData, &QueryFilter))
return false;
SCENE_QUERY_COLLECT_ALL();
return true;
@@ -2451,17 +2462,14 @@ void PhysicsBackend::AddRigidDynamicActorTorque(void* actor, const Vector3& torq
actorPhysX->addTorque(C2P(torque), static_cast<PxForceMode::Enum>(mode));
}
void* PhysicsBackend::CreateShape(PhysicsColliderActor* collider, const CollisionShape& geometry, JsonAsset* material, bool enabled, bool trigger)
void* PhysicsBackend::CreateShape(PhysicsColliderActor* collider, const CollisionShape& geometry, Span<JsonAsset*> materials, bool enabled, bool trigger)
{
const PxShapeFlags shapeFlags = GetShapeFlags(trigger, enabled);
PxMaterial* materialPhysX = DefaultMaterial;
if (material && !material->WaitForLoaded() && material->Instance)
{
materialPhysX = (PxMaterial*)((PhysicalMaterial*)material->Instance)->GetPhysicsMaterial();
}
Array<PxMaterial*, InlinedAllocation<1>> materialsPhysX;
GetShapeMaterials(materialsPhysX, materials);
PxGeometryHolder geometryPhysX;
GetShapeGeometry(geometry, geometryPhysX);
PxShape* shapePhysX = PhysX->createShape(geometryPhysX.any(), *materialPhysX, true, shapeFlags);
PxShape* shapePhysX = PhysX->createShape(geometryPhysX.any(), materialsPhysX.Get(), materialsPhysX.Count(), true, shapeFlags);
shapePhysX->userData = collider;
#if PHYSX_DEBUG_NAMING
shapePhysX->setName("Shape");
@@ -2551,15 +2559,12 @@ void PhysicsBackend::SetShapeContactOffset(void* shape, float value)
shapePhysX->setContactOffset(Math::Max(shapePhysX->getRestOffset() + ZeroTolerance, value));
}
void PhysicsBackend::SetShapeMaterial(void* shape, JsonAsset* material)
void PhysicsBackend::SetShapeMaterials(void* shape, Span<JsonAsset*> materials)
{
auto shapePhysX = (PxShape*)shape;
PxMaterial* materialPhysX = DefaultMaterial;
if (material && !material->WaitForLoaded() && material->Instance)
{
materialPhysX = (PxMaterial*)((PhysicalMaterial*)material->Instance)->GetPhysicsMaterial();
}
shapePhysX->setMaterials(&materialPhysX, 1);
Array<PxMaterial*, InlinedAllocation<1>> materialsPhysX;
GetShapeMaterials(materialsPhysX, materials);
shapePhysX->setMaterials(materialsPhysX.Get(), materialsPhysX.Count());
}
void PhysicsBackend::SetShapeGeometry(void* shape, const CollisionShape& geometry)
@@ -2608,9 +2613,8 @@ bool PhysicsBackend::RayCastShape(void* shape, const Vector3& position, const Qu
auto shapePhysX = (PxShape*)shape;
const Vector3 sceneOrigin = SceneOrigins[shapePhysX->getActor() ? shapePhysX->getActor()->getScene() : nullptr];
const PxTransform trans(C2P(position - sceneOrigin), C2P(orientation));
const PxHitFlags hitFlags = (PxHitFlags)0;
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin - sceneOrigin), C2P(direction), shapePhysX->getGeometry(), trans, maxDistance, hitFlags, 1, &hit) != 0)
if (PxGeometryQuery::raycast(C2P(origin - sceneOrigin), C2P(direction), shapePhysX->getGeometry(), trans, maxDistance, PxHitFlagEmpty, 1, &hit) != 0)
{
resultHitDistance = hit.distance;
return true;
@@ -2623,10 +2627,10 @@ bool PhysicsBackend::RayCastShape(void* shape, const Vector3& position, const Qu
auto shapePhysX = (PxShape*)shape;
const Vector3 sceneOrigin = SceneOrigins[shapePhysX->getActor() ? shapePhysX->getActor()->getScene() : nullptr];
const PxTransform trans(C2P(position - sceneOrigin), C2P(orientation));
const PxHitFlags hitFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX | PxHitFlag::eUV;
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin - sceneOrigin), C2P(direction), shapePhysX->getGeometry(), trans, maxDistance, hitFlags, 1, &hit) == 0)
if (PxGeometryQuery::raycast(C2P(origin - sceneOrigin), C2P(direction), shapePhysX->getGeometry(), trans, maxDistance, SCENE_QUERY_FLAGS, 1, &hit) == 0)
return false;
hit.shape = shapePhysX;
P2C(hit, hitInfo);
hitInfo.Point += sceneOrigin;
return true;
@@ -3004,7 +3008,7 @@ void* PhysicsBackend::CreateController(void* scene, IPhysicsActor* actor, Physic
desc.material = DefaultMaterial;
const float minSize = 0.001f;
desc.height = Math::Max(height, minSize);
desc.radius = Math::Max(radius - desc.contactOffset, minSize);
desc.radius = Math::Max(radius - Math::Max(contactOffset, 0.0f), minSize);
desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - minSize);
auto controllerPhysX = (PxCapsuleController*)scenePhysX->ControllerManager->createController(desc);
PxRigidActor* actorPhysX = controllerPhysX->getActor();
@@ -4081,10 +4085,17 @@ void PhysicsBackend::GetHeightFieldSize(void* heightField, int32& rows, int32& c
columns = (int32)heightFieldPhysX->getNbColumns();
}
float PhysicsBackend::GetHeightFieldHeight(void* heightField, float x, float z)
float PhysicsBackend::GetHeightFieldHeight(void* heightField, int32 x, int32 z)
{
auto heightFieldPhysX = (PxHeightField*)heightField;
return heightFieldPhysX->getHeight(x, z);
return heightFieldPhysX->getHeight((float)x, (float)z);
}
PhysicsBackend::HeightFieldSample PhysicsBackend::GetHeightFieldSample(void* heightField, int32 x, int32 z)
{
auto heightFieldPhysX = (PxHeightField*)heightField;
auto sample = heightFieldPhysX->getSample(x, z);
return { sample.height, sample.materialIndex0, sample.materialIndex1 };
}
bool PhysicsBackend::ModifyHeightField(void* heightField, int32 startCol, int32 startRow, int32 cols, int32 rows, const HeightFieldSample* data)

View File

@@ -38,10 +38,6 @@ void SimulationEventCallback::Clear()
BrokenJoints.Clear();
}
void SimulationEventCallback::CollectResults()
{
}
void SimulationEventCallback::SendCollisionEvents()
{
for (auto& c : RemovedCollisions)
@@ -132,7 +128,6 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c
//const PxU32 flippedContacts = (pair.flags & PxContactPairFlag::eINTERNAL_CONTACTS_ARE_FLIPPED);
const bool hasImpulses = pair.flags.isSet(PxContactPairFlag::eINTERNAL_HAS_IMPULSES);
const bool hasPostVelocities = !pair.flags.isSet(PxContactPairFlag::eACTOR_PAIR_LOST_TOUCH);
PxU32 nbContacts = 0;
PxVec3 totalImpulse(0.0f);
c.ThisActor = static_cast<PhysicsColliderActor*>(pair.shapes[0]->userData);
@@ -144,48 +139,38 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c
}
// Extract contact points
c.ContactsCount = 0;
while (i.hasNextPatch())
{
i.nextPatch();
while (i.hasNextContact() && nbContacts < COLLISION_NAX_CONTACT_POINTS)
while (i.hasNextContact() && c.ContactsCount < COLLISION_NAX_CONTACT_POINTS)
{
i.nextContact();
const PxVec3 point = i.getContactPoint();
const PxVec3 normal = i.getContactNormal();
if (hasImpulses)
totalImpulse += normal * impulses[nbContacts];
totalImpulse += normal * impulses[c.ContactsCount];
//PxU32 internalFaceIndex0 = flippedContacts ? iter.getFaceIndex1() : iter.getFaceIndex0();
//PxU32 internalFaceIndex1 = flippedContacts ? iter.getFaceIndex0() : iter.getFaceIndex1();
ContactPoint& contact = c.Contacts[nbContacts];
ContactPoint& contact = c.Contacts[c.ContactsCount++];
contact.Point = P2C(point);
contact.Normal = P2C(normal);
contact.Separation = i.getSeparation();
nbContacts++;
}
}
c.Impulse = P2C(totalImpulse);
// Extract velocities
c.ThisVelocity = c.OtherVelocity = Vector3::Zero;
if (hasPostVelocities && j.nextItemSet())
{
ASSERT(j.contactPairIndex == pairIndex);
ASSERT_LOW_LAYER(j.contactPairIndex == pairIndex);
if (j.postSolverVelocity)
{
const PxVec3 linearVelocityActor0 = j.postSolverVelocity->linearVelocity[0];
const PxVec3 linearVelocityActor1 = j.postSolverVelocity->linearVelocity[1];
c.ThisVelocity = P2C(linearVelocityActor0);
c.OtherVelocity = P2C(linearVelocityActor1);
c.ThisVelocity = P2C(j.postSolverVelocity->linearVelocity[0]);
c.OtherVelocity = P2C(j.postSolverVelocity->linearVelocity[1]);
}
}
c.ContactsCount = nbContacts;
c.Impulse = P2C(totalImpulse);
if (pair.flags & PxContactPairFlag::eACTOR_PAIR_HAS_FIRST_TOUCH)
{
NewCollisions.Add(c);

View File

@@ -49,11 +49,6 @@ public:
/// </summary>
void Clear();
/// <summary>
/// Generates the new/old/removed collisions and a valid trigger pairs.
/// </summary>
void CollectResults();
/// <summary>
/// Sends the collision events to the managed objects.
/// </summary>

View File

@@ -17,6 +17,7 @@
#include <ThirdParty/PhysX/foundation/PxBounds3.h>
#include <ThirdParty/PhysX/characterkinematic/PxExtended.h>
#include <ThirdParty/PhysX/PxShape.h>
#include <ThirdParty/PhysX/PxMaterial.h>
#include <ThirdParty/PhysX/PxQueryReport.h>
namespace physx
@@ -233,12 +234,28 @@ inline float RadPerSToRpm(float v)
return v * (30.0f / PI);
}
inline PhysicalMaterial* GetMaterial(const PxShape* shape, PxU32 faceIndex)
{
if (faceIndex != 0xFFFFffff)
{
PxBaseMaterial* mat = shape->getMaterialFromInternalFaceIndex(faceIndex);
return mat ? (PhysicalMaterial*)mat->userData : nullptr;
}
else
{
PxMaterial* mat;
shape->getMaterials(&mat, 1);
return mat ? (PhysicalMaterial*)mat->userData : nullptr;
}
}
inline void P2C(const PxRaycastHit& hit, RayCastHit& result)
{
result.Point = P2C(hit.position);
result.Normal = P2C(hit.normal);
result.Distance = hit.distance;
result.Collider = hit.shape ? static_cast<PhysicsColliderActor*>(hit.shape->userData) : nullptr;
result.Material = hit.shape ? GetMaterial(hit.shape, hit.faceIndex) : nullptr;
result.FaceIndex = hit.faceIndex;
result.UV.X = hit.u;
result.UV.Y = hit.v;
@@ -250,6 +267,7 @@ inline void P2C(const PxSweepHit& hit, RayCastHit& result)
result.Normal = P2C(hit.normal);
result.Distance = hit.distance;
result.Collider = hit.shape ? static_cast<PhysicsColliderActor*>(hit.shape->userData) : nullptr;
result.Material = hit.shape ? GetMaterial(hit.shape, hit.faceIndex) : nullptr;
result.FaceIndex = hit.faceIndex;
result.UV = Vector2::Zero;
}

View File

@@ -4,27 +4,21 @@
#include "Types.h"
#include "Engine/Core/ISerializable.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Level/Tags.h"
/// <summary>
/// Physical materials are used to define the response of a physical object when interacting dynamically with the world.
/// </summary>
API_CLASS(Attributes = "ContentContextMenu(\"New/Physics/Physical Material\")") class FLAXENGINE_API PhysicalMaterial final : public ISerializable
API_CLASS(Attributes = "ContentContextMenu(\"New/Physics/Physical Material\")")
class FLAXENGINE_API PhysicalMaterial final : public ScriptingObject, public ISerializable
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE_MINIMAL(PhysicalMaterial);
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(PhysicalMaterial, ScriptingObject);
private:
void* _material;
void* _material = nullptr;
public:
/// <summary>
/// Initializes a new instance of the <see cref="PhysicalMaterial"/> class.
/// </summary>
PhysicalMaterial();
/// <summary>
/// Finalizes an instance of the <see cref="PhysicalMaterial"/> class.
/// </summary>
~PhysicalMaterial();
public:

View File

@@ -78,11 +78,6 @@ void PhysicsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier*
}
}
PhysicalMaterial::PhysicalMaterial()
: _material(nullptr)
{
}
PhysicalMaterial::~PhysicalMaterial()
{
if (_material)

View File

@@ -4,6 +4,7 @@
#include "Physics.h"
#include "PhysicsSettings.h"
#include "Engine/Core/Types/Span.h"
struct HingeJointDrive;
struct SpringParameters;
@@ -182,7 +183,7 @@ public:
static void AddRigidDynamicActorTorque(void* actor, const Vector3& torque, ForceMode mode);
// Shapes
static void* CreateShape(PhysicsColliderActor* collider, const CollisionShape& geometry, JsonAsset* material, bool enabled, bool trigger);
static void* CreateShape(PhysicsColliderActor* collider, const CollisionShape& geometry, Span<JsonAsset*> materials, bool enabled, bool trigger);
static void SetShapeState(void* shape, bool enabled, bool trigger);
static void SetShapeFilterMask(void* shape, uint32 mask0, uint32 mask1);
static void* GetShapeActor(void* shape);
@@ -191,7 +192,7 @@ public:
static void GetShapeLocalPose(void* shape, Vector3& position, Quaternion& orientation);
static void SetShapeLocalPose(void* shape, const Vector3& position, const Quaternion& orientation);
static void SetShapeContactOffset(void* shape, float value);
static void SetShapeMaterial(void* shape, JsonAsset* material);
static void SetShapeMaterials(void* shape, Span<JsonAsset*> materials);
static void SetShapeGeometry(void* shape, const CollisionShape& geometry);
static void AttachShape(void* shape, void* actor);
static void DetachShape(void* shape, void* actor);
@@ -303,7 +304,8 @@ public:
static void GetTriangleMeshTriangles(void* triangleMesh, Array<Float3, HeapAllocation>& vertexBuffer, Array<int32, HeapAllocation>& indexBuffer);
static const uint32* GetTriangleMeshRemap(void* triangleMesh, uint32& count);
static void GetHeightFieldSize(void* heightField, int32& rows, int32& columns);
static float GetHeightFieldHeight(void* heightField, float x, float z);
static float GetHeightFieldHeight(void* heightField, int32 x, int32 z);
static HeightFieldSample GetHeightFieldSample(void* heightField, int32 x, int32 z);
static bool ModifyHeightField(void* heightField, int32 startCol, int32 startRow, int32 cols, int32 rows, const HeightFieldSample* data);
static void FlushRequests();
static void FlushRequests(void* scene);
@@ -330,6 +332,14 @@ public:
flags = (RigidDynamicFlags)(((uint32)flags & ~(uint32)flag) | (value ? (uint32)flag : 0));
SetRigidDynamicActorFlags(actor, flags);
}
FORCE_INLINE static void* CreateShape(PhysicsColliderActor* collider, const CollisionShape& geometry, JsonAsset* material, bool enabled, bool trigger)
{
return CreateShape(collider, geometry, Span<JsonAsset*>(&material, 1), enabled, trigger);
}
FORCE_INLINE static void SetShapeMaterial(void* shape, JsonAsset* material)
{
SetShapeMaterials(shape, Span<JsonAsset*>(&material, 1));
}
};
DECLARE_ENUM_OPERATORS(PhysicsBackend::ActorFlags);

View File

@@ -408,7 +408,7 @@ void PhysicsBackend::AddRigidDynamicActorTorque(void* actor, const Vector3& torq
{
}
void* PhysicsBackend::CreateShape(PhysicsColliderActor* collider, const CollisionShape& geometry, JsonAsset* material, bool enabled, bool trigger)
void* PhysicsBackend::CreateShape(PhysicsColliderActor* collider, const CollisionShape& geometry, Span<JsonAsset*> materials, bool enabled, bool trigger)
{
return DUMY_HANDLE;
}
@@ -447,7 +447,7 @@ void PhysicsBackend::SetShapeContactOffset(void* shape, float value)
{
}
void PhysicsBackend::SetShapeMaterial(void* shape, JsonAsset* material)
void PhysicsBackend::SetShapeMaterials(void* shape, Span<JsonAsset*> materials)
{
}
@@ -826,11 +826,16 @@ void PhysicsBackend::GetHeightFieldSize(void* heightField, int32& rows, int32& c
columns = 0;
}
float PhysicsBackend::GetHeightFieldHeight(void* heightField, float x, float z)
float PhysicsBackend::GetHeightFieldHeight(void* heightField, int32 x, int32 z)
{
return 0.0f;
}
PhysicsBackend::HeightFieldSample PhysicsBackend::GetHeightFieldSample(void* heightField, int32 x, int32 z)
{
return HeightFieldSample();
}
bool PhysicsBackend::ModifyHeightField(void* heightField, int32 startCol, int32 startRow, int32 cols, int32 rows, const HeightFieldSample* data)
{
return true;

View File

@@ -10,6 +10,7 @@
struct PhysicsStatistics;
class PhysicsColliderActor;
class PhysicsScene;
class PhysicalMaterial;
class Joint;
class Collider;
class CollisionData;
@@ -132,7 +133,7 @@ DECLARE_ENUM_OPERATORS(RigidbodyConstraints);
/// <summary>
/// Raycast hit result data.
/// </summary>
API_STRUCT() struct RayCastHit
API_STRUCT(NoDefault) struct RayCastHit
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(RayCastHit);
@@ -141,6 +142,11 @@ API_STRUCT() struct RayCastHit
/// </summary>
API_FIELD() PhysicsColliderActor* Collider = nullptr;
/// <summary>
/// The physical material of the surface that was hit.
/// </summary>
API_FIELD() PhysicalMaterial* Material = nullptr;
/// <summary>
/// The normal of the surface the ray hit.
/// </summary>
@@ -151,17 +157,17 @@ API_STRUCT() struct RayCastHit
/// </summary>
API_FIELD() float Distance;
/// <summary>
/// The point in the world space where ray hit the collider.
/// </summary>
API_FIELD() Vector3 Point;
/// <summary>
/// The index of the face that was hit. Valid only for convex mesh (polygon index), triangle mesh (triangle index) and height field (triangle index).
/// </summary>
/// <seealso cref="CollisionData.GetModelTriangle" />
API_FIELD() uint32 FaceIndex;
/// <summary>
/// The point in the world space where ray hit the collider.
/// </summary>
API_FIELD() Vector3 Point;
/// <summary>
/// The barycentric coordinates of hit triangle. Valid only for triangle mesh and height field.
/// </summary>

View File

@@ -41,6 +41,10 @@ static_assert(sizeof(bool) == 1, "Invalid bool type size.");
static_assert(sizeof(float) == 4, "Invalid float type size.");
static_assert(sizeof(double) == 8, "Invalid double type size.");
// Check configuration
static_assert((PLATFORM_THREADS_LIMIT & (PLATFORM_THREADS_LIMIT - 1)) == 0, "Threads limit must be power of two.");
static_assert(PLATFORM_THREADS_LIMIT % 4 == 0, "Threads limit must be multiple of 4.");
float PlatformBase::CustomDpiScale = 1.0f;
Array<User*, FixedAllocation<8>> PlatformBase::Users;
Delegate<User*> PlatformBase::UserAdded;

View File

@@ -100,9 +100,6 @@ WindowBase::WindowBase(const CreateWindowSettings& settings)
, _dpi(96)
, _dpiScale(1.0f)
, _trackingMouseOffset(Float2::Zero)
, _isUsingMouseOffset(false)
, _isTrackingMouse(false)
, _isClippingCursor(false)
, RenderTask(nullptr)
{
// Update window location based on start location

View File

@@ -284,12 +284,12 @@ protected:
float _dpiScale;
Float2 _trackingMouseOffset;
bool _isUsingMouseOffset;
bool _isUsingMouseOffset = false;
Rectangle _mouseOffsetScreenSize;
bool _isTrackingMouse;
bool _isHorizontalFlippingMouse;
bool _isVerticalFlippingMouse;
bool _isClippingCursor;
bool _isTrackingMouse = false;
bool _isHorizontalFlippingMouse = false;
bool _isVerticalFlippingMouse = false;
bool _isClippingCursor = false;
explicit WindowBase(const CreateWindowSettings& settings);
virtual ~WindowBase();

View File

@@ -7,6 +7,8 @@
#include "Engine/Threading/Threading.h"
#include "IncludeFreeType.h"
Array<AssetReference<FontAsset>, HeapAllocation> Font::FallbackFonts;
Font::Font(FontAsset* parentAsset, float size)
: ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer))
, _asset(parentAsset)
@@ -32,7 +34,7 @@ Font::~Font()
_asset->_fonts.Remove(this);
}
void Font::GetCharacter(Char c, FontCharacterEntry& result)
void Font::GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback)
{
// Try to get the character or cache it if cannot be found
if (!_characters.TryGet(c, result))
@@ -44,8 +46,23 @@ void Font::GetCharacter(Char c, FontCharacterEntry& result)
if (_characters.TryGet(c, result))
return;
// Try to use fallback font if character is missing
if (enableFallback && !_asset->ContainsChar(c))
{
for (int32 fallbackIndex = 0; fallbackIndex < FallbackFonts.Count(); fallbackIndex++)
{
FontAsset* fallbackFont = FallbackFonts.Get()[fallbackIndex].Get();
if (fallbackFont && fallbackFont->ContainsChar(c))
{
fallbackFont->CreateFont(GetSize())->GetCharacter(c, result, enableFallback);
return;
}
}
}
// Create character cache
FontManager::AddNewEntry(this, c, result);
ASSERT(result.Font);
// Add to the dictionary
_characters.Add(c, result);
@@ -87,7 +104,7 @@ void Font::CacheText(const StringView& text)
FontCharacterEntry entry;
for (int32 i = 0; i < text.Length(); i++)
{
GetCharacter(text[i], entry);
GetCharacter(text[i], entry, false);
}
}
@@ -104,12 +121,14 @@ void Font::Invalidate()
void Font::ProcessText(const StringView& text, Array<FontLineCache>& outputLines, const TextLayoutOptions& layout)
{
int32 textLength = text.Length();
if (textLength == 0)
return;
float cursorX = 0;
int32 kerning;
FontLineCache tmpLine;
FontCharacterEntry entry;
FontCharacterEntry previous;
int32 textLength = text.Length();
float scale = layout.Scale / FontManager::FontScale;
float boundsWidth = layout.Bounds.GetWidth();
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
@@ -157,7 +176,7 @@ void Font::ProcessText(const StringView& text, Array<FontLineCache>& outputLines
// Get kerning
if (!isWhitespace && previous.IsValid)
{
kerning = GetKerning(previous.Character, entry.Character);
kerning = entry.Font->GetKerning(previous.Character, entry.Character);
}
else
{
@@ -178,8 +197,8 @@ void Font::ProcessText(const StringView& text, Array<FontLineCache>& outputLines
if (lastWrapCharIndex != INVALID_INDEX)
{
// Skip moving twice for the same character
int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000;
if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2)
int32 lastLineLastCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000;
if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2)
{
currentIndex = nextCharIndex;
lastMoveLine = moveLine;
@@ -238,7 +257,8 @@ void Font::ProcessText(const StringView& text, Array<FontLineCache>& outputLines
lastMoveLine = moveLine;
}
if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n'))
// Check if an additional line should be created
if (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n')
{
// Add line
tmpLine.Size.X = cursorX;
@@ -341,7 +361,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te
// Apply kerning
if (!isWhitespace && previous.IsValid)
{
x += GetKerning(previous.Character, entry.Character);
x += entry.Font->GetKerning(previous.Character, entry.Character);
}
previous = entry;
@@ -415,7 +435,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
// Apply kerning
if (!isWhitespace && previous.IsValid)
{
x += GetKerning(previous.Character, entry.Character);
x += entry.Font->GetKerning(previous.Character, entry.Character);
}
previous = entry;

View File

@@ -18,9 +18,9 @@ struct FontTextureAtlasSlot;
/// <summary>
/// The text range.
/// </summary>
API_STRUCT(NoDefault) struct TextRange
API_STRUCT(NoDefault) struct FLAXENGINE_API TextRange
{
DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
/// <summary>
/// The start index (inclusive).
@@ -35,7 +35,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
/// <summary>
/// Gets the range length.
/// </summary>
int32 Length() const
FORCE_INLINE int32 Length() const
{
return EndIndex - StartIndex;
}
@@ -43,7 +43,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
/// <summary>
/// Gets a value indicating whether range is empty.
/// </summary>
bool IsEmpty() const
FORCE_INLINE bool IsEmpty() const
{
return (EndIndex - StartIndex) <= 0;
}
@@ -53,7 +53,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
/// </summary>
/// <param name="index">The index.</param>
/// <returns><c>true</c> if range contains the specified character index; otherwise, <c>false</c>.</returns>
bool Contains(int32 index) const
FORCE_INLINE bool Contains(int32 index) const
{
return index >= StartIndex && index < EndIndex;
}
@@ -88,9 +88,9 @@ struct TIsPODType<TextRange>
/// <summary>
/// The font line info generated during text processing.
/// </summary>
API_STRUCT(NoDefault) struct FontLineCache
API_STRUCT(NoDefault) struct FLAXENGINE_API FontLineCache
{
DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache);
DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache);
/// <summary>
/// The root position of the line (upper left corner).
@@ -108,7 +108,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache);
API_FIELD() int32 FirstCharIndex;
/// <summary>
/// The last character index (from the input text).
/// The last character index (from the input text), inclusive.
/// </summary>
API_FIELD() int32 LastCharIndex;
};
@@ -152,9 +152,9 @@ struct TIsPODType<FontLineCache>
/// <summary>
/// The cached font character entry (read for rendering and further processing).
/// </summary>
API_STRUCT(NoDefault) struct FontCharacterEntry
API_STRUCT(NoDefault) struct FLAXENGINE_API FontCharacterEntry
{
DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry);
DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry);
/// <summary>
/// The character represented by this entry.
@@ -210,6 +210,11 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry);
/// The slot in texture atlas, containing the pixel data of the glyph.
/// </summary>
API_FIELD() const FontTextureAtlasSlot* Slot;
/// <summary>
/// The owner font.
/// </summary>
API_FIELD() const class Font* Font;
};
template<>
@@ -223,10 +228,10 @@ struct TIsPODType<FontCharacterEntry>
/// </summary>
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API Font : public ManagedScriptingObject
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font);
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font);
friend FontAsset;
private:
private:
FontAsset* _asset;
float _size;
int32 _height;
@@ -238,7 +243,6 @@ private:
mutable Dictionary<uint32, int32> _kerningTable;
public:
/// <summary>
/// Initializes a new instance of the <see cref="Font"/> class.
/// </summary>
@@ -252,6 +256,10 @@ public:
~Font();
public:
/// <summary>
/// The active fallback fonts.
/// </summary>
API_FIELD() static Array<AssetReference<FontAsset>, HeapAllocation> FallbackFonts;
/// <summary>
/// Gets parent font asset that contains font family used by this font.
@@ -302,13 +310,13 @@ public:
}
public:
/// <summary>
/// Gets character entry.
/// </summary>
/// <param name="c">The character.</param>
/// <param name="result">The output character entry.</param>
void GetCharacter(Char c, FontCharacterEntry& result);
/// <param name="enableFallback">True if fallback to secondary font when the primary font doesn't contains this character.</param>
void GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback = true);
/// <summary>
/// Gets the kerning amount for a pair of characters.
@@ -330,7 +338,6 @@ public:
API_FUNCTION() void Invalidate();
public:
/// <summary>
/// Processes text to get cached lines for rendering.
/// </summary>
@@ -524,7 +531,6 @@ public:
void FlushFaceSize() const;
public:
// [Object]
String ToString() const override;
};

View File

@@ -199,14 +199,29 @@ bool FontAsset::Save(const StringView& path)
#endif
bool FontAsset::ContainsChar(Char c) const
{
return FT_Get_Char_Index(_face, c) > 0;
}
void FontAsset::Invalidate()
{
ScopeLock lock(Locker);
for (auto font : _fonts)
{
font->Invalidate();
}
}
uint64 FontAsset::GetMemoryUsage() const
{
Locker.Lock();
uint64 result = BinaryAsset::GetMemoryUsage();
result += sizeof(FontAsset) - sizeof(BinaryAsset);
result += sizeof(FT_FaceRec);
result += _fontFile.Length();
for (auto font : _fonts)
result += sizeof(Font);
Locker.Unlock();
return result;
}
bool FontAsset::init(AssetInitData& initData)

View File

@@ -93,6 +93,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API FontAsset : public BinaryAsset
{
DECLARE_BINARY_ASSET_HEADER(FontAsset, 3);
friend Font;
private:
FT_Face _face;
FontOptions _options;
@@ -174,11 +175,22 @@ public:
API_FUNCTION() bool Save(const StringView& path = StringView::Empty);
#endif
/// <summary>
/// Check if the font contains the glyph of a char.
/// </summary>
/// <param name="c">The char to test.</param>
/// <returns>True if the font contains the glyph of the char, otherwise false.</returns>
API_FUNCTION() bool ContainsChar(Char c) const;
/// <summary>
/// Invalidates all cached dynamic font atlases using this font. Can be used to reload font characters after changing font asset options.
/// </summary>
API_FUNCTION() void Invalidate();
public:
// [BinaryAsset]
uint64 GetMemoryUsage() const override;
protected:
// [BinaryAsset]
bool init(AssetInitData& initData) override;

View File

@@ -27,7 +27,6 @@ using namespace FontManagerImpl;
class FontManagerService : public EngineService
{
public:
FontManagerService()
: EngineService(TEXT("Font Manager"), -700)
{
@@ -155,6 +154,18 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry)
// Get the index to the glyph in the font face
const FT_UInt glyphIndex = FT_Get_Char_Index(face, c);
#if !BUILD_RELEASE
if (glyphIndex == 0)
{
LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c);
}
#endif
// Init the character data
Platform::MemoryClear(&entry, sizeof(entry));
entry.Character = c;
entry.Font = font;
entry.IsValid = false;
// Load the glyph
const FT_Error error = FT_Load_Glyph(face, glyphIndex, glyphFlags);
@@ -190,8 +201,6 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry)
ASSERT(bitmap && bitmap->pixel_mode == FT_PIXEL_MODE_GRAY);
// Fill the character data
Platform::MemoryClear(&entry, sizeof(entry));
entry.Character = c;
entry.AdvanceX = Convert26Dot6ToRoundedPixel<int16>(glyph->advance.x);
entry.OffsetY = glyph->bitmap_top;
entry.OffsetX = glyph->bitmap_left;

View File

@@ -27,11 +27,11 @@
#if USE_EDITOR
#define RENDER2D_CHECK_RENDERING_STATE \
if (!Render2D::IsRendering()) \
{ \
LOG(Error, "Calling Render2D is only valid during rendering."); \
return; \
}
if (!Render2D::IsRendering()) \
{ \
LOG(Error, "Calling Render2D is only valid during rendering."); \
return; \
}
#else
#define RENDER2D_CHECK_RENDERING_STATE
#endif
@@ -180,7 +180,7 @@ struct ClipMask
Rectangle Bounds;
};
Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping;
Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping | RenderingFeatures::FallbackFonts;
namespace
{
@@ -1137,8 +1137,8 @@ void DrawBatch(int32 startIndex, int32 count)
}
// Draw
Context->BindVB(ToSpan(&vb, 1)); // TODO: reduce bindings frequency
Context->BindIB(ib); // TODO: reduce bindings frequency
Context->BindVB(ToSpan(&vb, 1));
Context->BindIB(ib);
Context->DrawIndexed(countIb, 0, d.StartIB);
}
@@ -1159,6 +1159,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
FontCharacterEntry previous;
int32 kerning;
float scale = 1.0f / FontManager::FontScale;
const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts);
// Render all characters
FontCharacterEntry entry;
@@ -1183,7 +1184,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
if (currentChar != '\n')
{
// Get character entry
font->GetCharacter(currentChar, entry);
font->GetCharacter(currentChar, entry, enableFallbackFonts);
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex)
@@ -1210,7 +1211,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
// Get kerning
if (!isWhitespace && previous.IsValid)
{
kerning = font->GetKerning(previous.Character, entry.Character);
kerning = entry.Font->GetKerning(previous.Character, entry.Character);
}
else
{
@@ -1273,6 +1274,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
FontCharacterEntry previous;
int32 kerning;
float scale = layout.Scale / FontManager::FontScale;
const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts);
// Process text to get lines
Lines.Clear();
@@ -1299,10 +1301,14 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
// Render all characters from the line
for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex; charIndex++)
{
const Char c = text[charIndex];
if (c != '\n')
// Cache current character
const Char currentChar = text[charIndex];
// Check if it isn't a newline character
if (currentChar != '\n')
{
font->GetCharacter(c, entry);
// Get character entry
font->GetCharacter(currentChar, entry, enableFallbackFonts);
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex)
@@ -1324,10 +1330,10 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
}
// Get kerning
const bool isWhitespace = StringUtils::IsWhitespace(c);
const bool isWhitespace = StringUtils::IsWhitespace(currentChar);
if (!isWhitespace && previous.IsValid)
{
kerning = font->GetKerning(previous.Character, entry.Character);
kerning = entry.Font->GetKerning(previous.Character, entry.Character);
}
else
{
@@ -1931,7 +1937,7 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength)
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs)
{
RENDER2D_CHECK_RENDERING_STATE;
CHECK(vertices.Length() == uvs.Length())
CHECK(vertices.Length() == uvs.Length());
Render2DDrawCall& drawCall = DrawCalls.AddOne();
drawCall.Type = DrawCallType::FillTexture;
@@ -1977,7 +1983,7 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<uint16>& indices,
drawCall.StartIB = IBIndex;
drawCall.CountIB = indices.Length();
drawCall.AsTexture.Ptr = t;
for (int32 i = 0; i < indices.Length();)
{
const uint16 i0 = indices.Get()[i++];

View File

@@ -44,6 +44,11 @@ API_CLASS(Static) class FLAXENGINE_API Render2D
/// Enables automatic geometry vertices snapping to integer coordinates in screen space. Reduces aliasing and sampling artifacts. Might be disabled for 3D projection viewport or for complex UI transformations.
/// </summary>
VertexSnapping = 1,
/// <summary>
/// Enables automatic characters usage from fallback fonts.
/// </summary>
FallbackFonts = 2,
};
struct CustomData
@@ -452,3 +457,5 @@ public:
/// <param name="color">The color.</param>
API_FUNCTION() static void FillTriangle(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color);
};
DECLARE_ENUM_OPERATORS(Render2D::RenderingFeatures);

View File

@@ -61,6 +61,16 @@ namespace FlaxEngine
/// </summary>
public float Spacing;
/// <summary>
/// The minimum size of the collection.
/// </summary>
public int MinCount;
/// <summary>
/// The maximum size of the collection. Zero if unlimited.
/// </summary>
public int MaxCount;
/// <summary>
/// The collection background color.
/// </summary>

View File

@@ -0,0 +1,25 @@
using System;
namespace FlaxEngine;
/// <summary>
/// This attribute is used to check for if a script requires an Actor type.
/// </summary>
[Serializable]
[AttributeUsage(AttributeTargets.Class)]
public class RequireActorAttribute : Attribute
{
/// <summary>
/// The required type.
/// </summary>
public Type RequiredType;
/// <summary>
/// Initializes a new instance of the <see cref="RequireActorAttribute"/> class.
/// </summary>
/// <param name="type">The required type.</param>
public RequireActorAttribute(Type type)
{
RequiredType = type;
}
}

View File

@@ -0,0 +1,34 @@
using System;
namespace FlaxEngine;
/// <summary>
/// This attribute is used to check for if a script requires other script types.
/// </summary>
[Serializable]
[AttributeUsage(AttributeTargets.Class)]
public class RequireScriptAttribute : Attribute
{
/// <summary>
/// The required types.
/// </summary>
public Type[] RequiredTypes;
/// <summary>
/// Initializes a new instance of the <see cref="RequireScriptAttribute"/> class.
/// </summary>
/// <param name="type">The required type.</param>
public RequireScriptAttribute(Type type)
{
RequiredTypes = new[] { type };
}
/// <summary>
/// Initializes a new instance of the <see cref="RequireScriptAttribute"/> class.
/// </summary>
/// <param name="types">The required types.</param>
public RequireScriptAttribute(Type[] types)
{
RequiredTypes = types;
}
}

View File

@@ -533,7 +533,12 @@ void ScriptingType::HackObjectVTable(void* object, ScriptingTypeHandle baseTypeH
if (!Script.VTable)
{
// Ensure to have valid Script VTable hacked
SetupScriptObjectVTable(object, baseTypeHandle, wrapperIndex);
BinaryModule::Locker.Lock();
if (!Script.VTable)
{
SetupScriptObjectVTable(object, baseTypeHandle, wrapperIndex);
}
BinaryModule::Locker.Unlock();
}
// Override object vtable with hacked one that has calls to overriden scripting functions

View File

@@ -68,4 +68,30 @@ public:
{
return FromString<EnumType>(StringAnsi(name));
}
// Gets the name of the enum value as separated flags
template<class EnumType>
static String ToStringFlags(EnumType value, Char separator = '|')
{
String result;
if (const auto items = GetItems<EnumType>())
{
for (int32 i = 0; items[i].Name; i++)
{
const uint64 itemValue = items[i].Value;
if ((uint64)value == 0 && itemValue == 0)
{
result = items[i].Name;
break;
}
if (itemValue != 0 && EnumHasAllFlags<EnumType>(value, (EnumType)itemValue))
{
if (result.HasChars())
result += separator;
result += items[i].Name;
}
}
}
return result;
}
};

View File

@@ -116,7 +116,7 @@ Action Scripting::ScriptsLoaded;
Action Scripting::ScriptsUnload;
Action Scripting::ScriptsReloading;
Action Scripting::ScriptsReloaded;
ThreadLocal<Scripting::IdsMappingTable*, PLATFORM_THREADS_LIMIT, true> Scripting::ObjectsLookupIdMapping;
ThreadLocal<Scripting::IdsMappingTable*, PLATFORM_THREADS_LIMIT> Scripting::ObjectsLookupIdMapping;
ScriptingService ScriptingServiceInstance;
bool initFlaxEngine();

View File

@@ -6,7 +6,7 @@
#include "Engine/Scripting/ScriptingType.h"
#include "Types.h"
template<typename T, int32 MaxThreads, bool ClearMemory>
template<typename T, int32 MaxThreads>
class ThreadLocal;
/// <summary>
@@ -114,7 +114,7 @@ public:
/// <summary>
/// The objects lookup identifier mapping used to override the object ids on FindObject call (used by the object references deserialization).
/// </summary>
static ThreadLocal<IdsMappingTable*, PLATFORM_THREADS_LIMIT, true> ObjectsLookupIdMapping;
static ThreadLocal<IdsMappingTable*, PLATFORM_THREADS_LIMIT> ObjectsLookupIdMapping;
/// <summary>
/// Finds the object by the given identifier. Searches registered scene objects and optionally assets. Logs warning if fails.

View File

@@ -12,7 +12,6 @@
class FLAXENGINE_API FileReadStream : public ReadStream
{
private:
File* _file;
uint32 _virtualPosInBuffer; // Current position in the buffer (index)
uint32 _bufferSize; // Amount of loaded bytes from the file to the buffer
@@ -33,11 +32,9 @@ public:
~FileReadStream();
public:
/// <summary>
/// Gets the file handle.
/// </summary>
/// <returns>File</returns>
FORCE_INLINE const File* GetFile() const
{
return _file;
@@ -49,7 +46,6 @@ public:
void Unlink();
public:
/// <summary>
/// Open file to write data to it
/// </summary>
@@ -58,7 +54,6 @@ public:
static FileReadStream* Open(const StringView& path);
public:
// [ReadStream]
void Flush() final override;
void Close() final override;

View File

@@ -84,7 +84,7 @@ bool cacheStaticGeometryTree(Actor* actor, ShadowsOfMordor::Builder::SceneBuildC
{
auto patch = terrain->GetPatch(patchIndex);
entry.AsTerrain.PatchIndex = patchIndex;
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto chunk = patch->Chunks[chunkIndex];
entry.AsTerrain.ChunkIndex = chunkIndex;

View File

@@ -165,7 +165,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context)
Matrix::Transpose(world, shaderData.WorldMatrix);
shaderData.LightmapArea = chunk->Lightmap.UVsArea;
shaderData.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
chunk->GetHeightmapUVScaleBias(&shaderData.HeightmapUVScaleBias);
shaderData.HeightmapUVScaleBias = chunk->GetHeightmapUVScaleBias();
// Extract per axis scales from LocalToWorld transform
const float scaleX = Float3(world.M11, world.M12, world.M13).Length();

View File

@@ -29,7 +29,7 @@ Terrain::Terrain(const SpawnParams& params)
, _cachedScale(1.0f)
{
_drawCategory = SceneRendering::SceneDrawAsync;
PhysicalMaterial.Changed.Bind<Terrain, &Terrain::OnPhysicalMaterialChanged>(this);
_physicalMaterials.Resize(8);
}
Terrain::~Terrain()
@@ -59,7 +59,7 @@ void Terrain::CacheNeighbors()
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
patch->Chunks[chunkIndex].CacheNeighbors();
}
@@ -185,7 +185,7 @@ bool Terrain::RayCast(const Vector3& origin, const Vector3& direction, RayCastHi
return result;
}
void Terrain::ClosestPoint(const Vector3& position, Vector3& result) const
void Terrain::ClosestPoint(const Vector3& point, Vector3& result) const
{
Real minDistance = MAX_Real;
Vector3 tmp;
@@ -194,8 +194,8 @@ void Terrain::ClosestPoint(const Vector3& position, Vector3& result) const
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->ClosestPoint(position, tmp);
const auto distance = Vector3::DistanceSquared(position, tmp);
patch->ClosestPoint(point, tmp);
const auto distance = Vector3::DistanceSquared(point, tmp);
if (distance < minDistance)
{
minDistance = distance;
@@ -205,12 +205,17 @@ void Terrain::ClosestPoint(const Vector3& position, Vector3& result) const
}
}
bool Terrain::ContainsPoint(const Vector3& point) const
{
return false;
}
void Terrain::DrawPatch(const RenderContext& renderContext, const Int2& patchCoord, MaterialBase* material, int32 lodIndex) const
{
auto patch = GetPatch(patchCoord);
if (patch)
{
for (int32 i = 0; i < TerrainPatch::CHUNKS_COUNT; i++)
for (int32 i = 0; i < Terrain::ChunksCount; i++)
patch->Chunks[i].Draw(renderContext, material, lodIndex);
}
}
@@ -228,22 +233,6 @@ void Terrain::DrawChunk(const RenderContext& renderContext, const Int2& patchCoo
}
}
void Terrain::OnPhysicalMaterialChanged()
{
if (_patches.IsEmpty())
return;
// Update the shapes material
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
PhysicsBackend::SetShapeMaterial(patch->_physicsShape, PhysicalMaterial.Get());
}
}
}
#if TERRAIN_USE_PHYSICS_DEBUG
void Terrain::DrawPhysicsDebug(RenderView& view)
@@ -295,6 +284,21 @@ void Terrain::SetCollisionLOD(int32 value)
#endif
}
void Terrain::SetPhysicalMaterials(const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& value)
{
_physicalMaterials = value;
_physicalMaterials.Resize(8);
JsonAsset* materials[8];
for (int32 i = 0;i<8;i++)
materials[i] = _physicalMaterials[i];
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches.Get()[pathIndex];
if (patch->HasCollision())
PhysicsBackend::SetShapeMaterials(patch->_physicsShape, ToSpan(materials, 8));
}
}
TerrainPatch* Terrain::GetPatch(const Int2& patchCoord) const
{
return GetPatch(patchCoord.X, patchCoord.Y);
@@ -540,7 +544,7 @@ void Terrain::Draw(RenderContext& renderContext)
Matrix localToWorld, worldToLocal;
BoundingSphere chunkSphere;
BoundingBox localBounds;
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
TerrainChunk* chunk = &patch->Chunks[chunkIndex];
chunk->GetTransform().GetWorld(localToWorld); // TODO: large-worlds
@@ -570,7 +574,7 @@ void Terrain::Draw(RenderContext& renderContext)
continue;
// Frustum vs Box culling for chunks
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto chunk = &patch->Chunks[chunkIndex];
chunk->_cachedDrawLOD = 0;
@@ -588,7 +592,7 @@ void Terrain::Draw(RenderContext& renderContext)
else
{
// Reset cached LOD for chunks (prevent LOD transition from invisible chunks)
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto chunk = &patch->Chunks[chunkIndex];
chunk->_cachedDrawLOD = 0;
@@ -616,10 +620,10 @@ void Terrain::OnDebugDrawSelected()
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
for (int32 chunkIndex = 0; chunkIndex < TerrainPatch::CHUNKS_COUNT; chunkIndex++)
for (int32 chunkIndex = 0; chunkIndex < Terrain::ChunksCount; chunkIndex++)
{
auto chunk = &patch->Chunks[chunkIndex];
DebugDraw::DrawBox(chunk->_bounds, Color(chunk->_x / (float)TerrainPatch::CHUNKS_COUNT_EDGE, 1.0f, chunk->_z / (float)TerrainPatch::CHUNKS_COUNT_EDGE));
DebugDraw::DrawBox(chunk->_bounds, Color(chunk->_x / (float)Terrain::ChunksCountEdge, 1.0f, chunk->_z / (float)Terrain::ChunksCountEdge));
}
}
*/
@@ -667,8 +671,8 @@ void Terrain::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(ScaleInLightmap, _scaleInLightmap);
SERIALIZE_MEMBER(BoundsExtent, _boundsExtent);
SERIALIZE_MEMBER(CollisionLOD, _collisionLod);
SERIALIZE_MEMBER(PhysicalMaterials, _physicalMaterials);
SERIALIZE(Material);
SERIALIZE(PhysicalMaterial);
SERIALIZE(DrawModes);
SERIALIZE_MEMBER(LODCount, _lodCount);
@@ -714,8 +718,8 @@ void Terrain::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
DESERIALIZE_MEMBER(LODDistribution, _lodDistribution);
DESERIALIZE_MEMBER(ScaleInLightmap, _scaleInLightmap);
DESERIALIZE_MEMBER(BoundsExtent, _boundsExtent);
DESERIALIZE_MEMBER(PhysicalMaterials, _physicalMaterials);
DESERIALIZE(Material);
DESERIALIZE(PhysicalMaterial);
DESERIALIZE(DrawModes);
member = stream.FindMember("LODCount");
@@ -780,6 +784,15 @@ void Terrain::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
// [Deprecated on 27.04.2022, expires on 27.04.2024]
if (modifier->EngineBuild <= 6331)
DrawModes |= DrawPass::GlobalSurfaceAtlas;
// [Deprecated on 15.02.2024, expires on 15.02.2026]
JsonAssetReference<PhysicalMaterial> PhysicalMaterial;
DESERIALIZE(PhysicalMaterial);
if (PhysicalMaterial)
{
for (auto& e : _physicalMaterials)
e = PhysicalMaterial;
}
}
RigidBody* Terrain::GetAttachedRigidBody() const

View File

@@ -2,7 +2,7 @@
#pragma once
#include "Engine/Content/JsonAsset.h"
#include "Engine/Content/JsonAssetReference.h"
#include "Engine/Content/Assets/MaterialBase.h"
#include "Engine/Physics/Actors/PhysicsColliderActor.h"
@@ -10,6 +10,7 @@ class Terrain;
class TerrainChunk;
class TerrainPatch;
class TerrainManager;
class PhysicalMaterial;
struct RayCastHit;
struct RenderView;
@@ -23,10 +24,7 @@ struct RenderView;
#define TERRAIN_EDITING 1
// Enable/disable terrain heightmap samples modification and gather. Used by the editor to modify the terrain with the brushes.
#define TERRAIN_UPDATING (USE_EDITOR)
// Enable/disable precise terrain geometry collision testing (with in-build vertex buffer caching, this will increase memory usage)
#define USE_PRECISE_TERRAIN_INTERSECTS (USE_EDITOR)
#define TERRAIN_UPDATING 1
// Enable/disable terrain physics collision drawing
#define TERRAIN_USE_PHYSICS_DEBUG (USE_EDITOR && 1)
@@ -41,13 +39,28 @@ struct RenderView;
/// <seealso cref="PhysicsColliderActor" />
API_CLASS(Sealed) class FLAXENGINE_API Terrain : public PhysicsColliderActor
{
DECLARE_SCENE_OBJECT(Terrain);
DECLARE_SCENE_OBJECT(Terrain);
friend Terrain;
friend TerrainPatch;
friend TerrainChunk;
private:
/// <summary>
/// Various defines regarding terrain configuration.
/// </summary>
API_ENUM() enum Config
{
/// <summary>
/// The maximum allowed amount of chunks per patch.
/// </summary>
ChunksCount = 16,
/// <summary>
/// The maximum allowed amount of chunks per chunk.
/// </summary>
ChunksCountEdge = 4,
};
private:
char _lodBias;
char _forcedLod;
char _collisionLod;
@@ -60,28 +73,21 @@ private:
Float3 _cachedScale;
Array<TerrainPatch*, InlinedAllocation<64>> _patches;
Array<TerrainChunk*> _drawChunks;
Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>> _physicalMaterials;
public:
/// <summary>
/// Finalizes an instance of the <see cref="Terrain"/> class.
/// </summary>
~Terrain();
public:
/// <summary>
/// The default material used for terrain rendering (chunks can override this).
/// </summary>
API_FIELD(Attributes="EditorOrder(100), DefaultValue(null), EditorDisplay(\"Terrain\")")
AssetReference<MaterialBase> Material;
/// <summary>
/// The physical material used to define the terrain collider physical properties.
/// </summary>
API_FIELD(Attributes="EditorOrder(520), DefaultValue(null), Limit(-1, 100, 0.1f), EditorDisplay(\"Collision\"), AssetReference(typeof(PhysicalMaterial), true)")
AssetReference<JsonAsset> PhysicalMaterial;
/// <summary>
/// The draw passes to use for rendering this object.
/// </summary>
@@ -89,7 +95,6 @@ public:
DrawPass DrawModes = DrawPass::Default;
public:
/// <summary>
/// Gets the terrain Level Of Detail bias value. Allows to increase or decrease rendered terrain quality.
/// </summary>
@@ -180,6 +185,21 @@ public:
/// </summary>
API_PROPERTY() void SetCollisionLOD(int32 value);
/// <summary>
/// Gets the list with physical materials used to define the terrain collider physical properties - each for terrain layer (layer index matches index in this array).
/// </summary>
API_PROPERTY(Attributes="EditorOrder(520), EditorDisplay(\"Collision\"), Collection(MinCount = 8, MaxCount = 8)")
FORCE_INLINE const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& GetPhysicalMaterials() const
{
return _physicalMaterials;
}
/// <summary>
/// Sets the list with physical materials used to define the terrain collider physical properties - each for terrain layer (layer index matches index in this array).
/// </summary>
API_PROPERTY()
void SetPhysicalMaterials(const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& value);
/// <summary>
/// Gets the terrain Level Of Detail count.
/// </summary>
@@ -219,7 +239,7 @@ public:
/// </summary>
/// <param name="patchCoord">The patch location (x and z).</param>
/// <returns>The patch.</returns>
TerrainPatch* GetPatch(const Int2& patchCoord) const;
API_FUNCTION() TerrainPatch* GetPatch(API_PARAM(Ref) const Int2& patchCoord) const;
/// <summary>
/// Gets the patch at the given location.
@@ -227,7 +247,7 @@ public:
/// <param name="x">The patch location x.</param>
/// <param name="z">The patch location z.</param>
/// <returns>The patch.</returns>
TerrainPatch* GetPatch(int32 x, int32 z) const;
API_FUNCTION() TerrainPatch* GetPatch(int32 x, int32 z) const;
/// <summary>
/// Gets the zero-based index of the terrain patch in the terrain patches collection.
@@ -241,7 +261,7 @@ public:
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The patch.</returns>
FORCE_INLINE TerrainPatch* GetPatch(int32 index) const
API_FUNCTION() FORCE_INLINE TerrainPatch* GetPatch(int32 index) const
{
return _patches[index];
}
@@ -311,9 +331,7 @@ public:
#endif
public:
#if TERRAIN_EDITING
/// <summary>
/// Setups the terrain. Clears the existing data.
/// </summary>
@@ -338,7 +356,6 @@ public:
/// </summary>
/// <param name="patchCoord">The patch location (x and z).</param>
API_FUNCTION() void RemovePatch(API_PARAM(Ref) const Int2& patchCoord);
#endif
/// <summary>
@@ -362,17 +379,6 @@ public:
void RemoveLightmap();
public:
/// <summary>
/// Performs a raycast against this terrain collision shape.
/// </summary>
/// <param name="origin">The origin of the ray.</param>
/// <param name="direction">The normalized direction of the ray.</param>
/// <param name="resultHitDistance">The raycast result hit position distance from the ray origin. Valid only if raycast hits anything.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against this terrain collision shape. Returns the hit chunk.
/// </summary>
@@ -382,7 +388,7 @@ public:
/// <param name="resultChunk">The raycast result hit chunk. Valid only if raycast hits anything.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, TerrainChunk*& resultChunk, float maxDistance = MAX_float) const;
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) float& resultHitDistance, API_PARAM(Out) TerrainChunk*& resultChunk, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against this terrain collision shape. Returns the hit chunk.
@@ -395,23 +401,6 @@ public:
/// <returns>True if ray hits an object, otherwise false.</returns>
API_FUNCTION() bool RayCast(const Ray& ray, API_PARAM(Out) float& resultHitDistance, API_PARAM(Out) Int2& resultPatchCoord, API_PARAM(Out) Int2& resultChunkCoord, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against terrain collision, returns results in a RayCastHit structure.
/// </summary>
/// <param name="origin">The origin of the ray.</param>
/// <param name="direction">The normalized direction of the ray.</param>
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <returns>True if ray hits an object, otherwise false.</returns>
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, float maxDistance = MAX_float) const;
/// <summary>
/// Gets a point on the terrain collider that is closest to a given location. Can be used to find a hit location or position to apply explosion force or any other special effects.
/// </summary>
/// <param name="position">The position to find the closest point to it.</param>
/// <param name="result">The result point on the collider that is closest to the specified location.</param>
API_FUNCTION() void ClosestPoint(const Vector3& position, API_PARAM(Out) Vector3& result) const;
/// <summary>
/// Draws the terrain patch.
/// </summary>
@@ -432,14 +421,11 @@ public:
API_FUNCTION() void DrawChunk(API_PARAM(Ref) const RenderContext& renderContext, API_PARAM(Ref) const Int2& patchCoord, API_PARAM(Ref) const Int2& chunkCoord, MaterialBase* material, int32 lodIndex = 0) const;
private:
void OnPhysicalMaterialChanged();
#if TERRAIN_USE_PHYSICS_DEBUG
void DrawPhysicsDebug(RenderView& view);
void DrawPhysicsDebug(RenderView& view);
#endif
public:
// [PhysicsColliderActor]
void Draw(RenderContext& renderContext) override;
#if USE_EDITOR
@@ -450,9 +436,12 @@ public:
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
RigidBody* GetAttachedRigidBody() const override;
bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance = MAX_float) const final;
bool RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance = MAX_float) const final;
void ClosestPoint(const Vector3& point, Vector3& result) const final;
bool ContainsPoint(const Vector3& point) const final;
protected:
// [PhysicsColliderActor]
void OnEnable() override;
void OnDisable() override;

View File

@@ -11,7 +11,14 @@
#include "Engine/Renderer/RenderList.h"
#include "Engine/Core/Math/OrientedBoundingBox.h"
#include "Engine/Level/Scene/Scene.h"
#if USE_EDITOR
#include "Engine/Level/Prefabs/PrefabManager.h"
#endif
TerrainChunk::TerrainChunk(const SpawnParams& params)
: ScriptingObject(params)
{
}
void TerrainChunk::Init(TerrainPatch* patch, uint16 x, uint16 z)
{
@@ -21,7 +28,7 @@ void TerrainChunk::Init(TerrainPatch* patch, uint16 x, uint16 z)
_z = z;
_yOffset = 0;
_yHeight = 1;
_heightmapUVScaleBias = Float4(1.0f, 1.0f, _x, _z) * (1.0f / TerrainPatch::CHUNKS_COUNT_EDGE);
_heightmapUVScaleBias = Float4(1.0f, 1.0f, _x, _z) * (1.0f / Terrain::ChunksCountEdge);
_perInstanceRandom = (_patch->_terrain->_id.C ^ _x ^ _z) * (1.0f / (float)MAX_uint32);
OverrideMaterial = nullptr;
}
@@ -51,8 +58,8 @@ bool TerrainChunk::PrepareDraw(const RenderContext& renderContext)
//lod = 0;
//lod = 10;
//lod = (_x + _z + TerrainPatch::CHUNKS_COUNT_EDGE * (_patch->_x + _patch->_z));
//lod = (int32)Vector2::Distance(Vector2(2, 2), Vector2(_patch->_x, _patch->_z) * TerrainPatch::CHUNKS_COUNT_EDGE + Vector2(_x, _z));
//lod = (_x + _z + Terrain::ChunksCountEdge * (_patch->_x + _patch->_z));
//lod = (int32)Vector2::Distance(Vector2(2, 2), Vector2(_patch->_x, _patch->_z) * Terrain::ChunksCountEdge + Vector2(_x, _z));
//lod = (int32)(Vector3::Distance(_bounds.GetCenter(), view.Position) / 10000.0f);
}
lod = Math::Clamp(lod, minStreamedLod, lodCount - 1);
@@ -93,7 +100,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
drawCall.ObjectRadius = _sphere.Radius;
drawCall.Terrain.Patch = _patch;
drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias;
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z));
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * Terrain::ChunksCountEdge + _x), (float)(_patch->_z * Terrain::ChunksCountEdge + _z));
drawCall.Terrain.CurrentLOD = (float)lod;
drawCall.Terrain.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1);
drawCall.Terrain.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
@@ -151,7 +158,7 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi
drawCall.ObjectRadius = _sphere.Radius;
drawCall.Terrain.Patch = _patch;
drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias;
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z));
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * Terrain::ChunksCountEdge + _x), (float)(_patch->_z * Terrain::ChunksCountEdge + _z));
drawCall.Terrain.CurrentLOD = (float)lod;
drawCall.Terrain.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1);
drawCall.Terrain.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
@@ -232,46 +239,46 @@ void TerrainChunk::CacheNeighbors()
_neighbors[0] = this;
if (_z > 0)
{
_neighbors[0] = &_patch->Chunks[(_z - 1) * TerrainPatch::CHUNKS_COUNT_EDGE + _x];
_neighbors[0] = &_patch->Chunks[(_z - 1) * Terrain::ChunksCountEdge + _x];
}
else
{
const auto patch = _patch->_terrain->GetPatch(_patch->_x, _patch->_z - 1);
if (patch)
_neighbors[0] = &patch->Chunks[(TerrainPatch::CHUNKS_COUNT_EDGE - 1) * TerrainPatch::CHUNKS_COUNT_EDGE + _x];
_neighbors[0] = &patch->Chunks[(Terrain::ChunksCountEdge - 1) * Terrain::ChunksCountEdge + _x];
}
// 1: left
_neighbors[1] = this;
if (_x > 0)
{
_neighbors[1] = &_patch->Chunks[_z * TerrainPatch::CHUNKS_COUNT_EDGE + (_x - 1)];
_neighbors[1] = &_patch->Chunks[_z * Terrain::ChunksCountEdge + (_x - 1)];
}
else
{
const auto patch = _patch->_terrain->GetPatch(_patch->_x - 1, _patch->_z);
if (patch)
_neighbors[1] = &patch->Chunks[_z * TerrainPatch::CHUNKS_COUNT_EDGE + (TerrainPatch::CHUNKS_COUNT_EDGE - 1)];
_neighbors[1] = &patch->Chunks[_z * Terrain::ChunksCountEdge + (Terrain::ChunksCountEdge - 1)];
}
// 2: right
_neighbors[2] = this;
if (_x < TerrainPatch::CHUNKS_COUNT_EDGE - 1)
if (_x < Terrain::ChunksCountEdge - 1)
{
_neighbors[2] = &_patch->Chunks[_z * TerrainPatch::CHUNKS_COUNT_EDGE + (_x + 1)];
_neighbors[2] = &_patch->Chunks[_z * Terrain::ChunksCountEdge + (_x + 1)];
}
else
{
const auto patch = _patch->_terrain->GetPatch(_patch->_x + 1, _patch->_z);
if (patch)
_neighbors[2] = &patch->Chunks[_z * TerrainPatch::CHUNKS_COUNT_EDGE];
_neighbors[2] = &patch->Chunks[_z * Terrain::ChunksCountEdge];
}
// 3: top
_neighbors[3] = this;
if (_z < TerrainPatch::CHUNKS_COUNT_EDGE - 1)
if (_z < Terrain::ChunksCountEdge - 1)
{
_neighbors[3] = &_patch->Chunks[(_z + 1) * TerrainPatch::CHUNKS_COUNT_EDGE + _x];
_neighbors[3] = &_patch->Chunks[(_z + 1) * Terrain::ChunksCountEdge + _x];
}
else
{

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