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