Merge branch 'FlaxEngine:master' into model-prefab-fix

This commit is contained in:
Menotdan
2024-02-18 12:29:45 -05:00
committed by GitHub
102 changed files with 1675 additions and 1097 deletions

View File

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

View File

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

View File

@@ -221,6 +221,7 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
context.NodePath.Clear();
context.Data = &data;
context.DeltaTime = dt;
context.StackOverFlow = false;
context.CurrentFrameIndex = ++data.CurrentFrame;
context.CallStack.Clear();
context.Functions.Clear();
@@ -411,9 +412,12 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box)
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

View File

@@ -796,6 +796,7 @@ 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1940,15 +1940,15 @@ void DebugDraw::DrawWireArc(const Vector3& position, const Quaternion& orientati
DrawLine(prevPos, world.GetTranslation(), color, duration, depthTest);
}
void DebugDraw::DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, const Color& color, float duration, bool depthTest)
void DebugDraw::DrawWireArrow(const Vector3& position, const Quaternion& orientation, float scale, float capScale, const Color& color, float duration, bool depthTest)
{
Float3 direction, up, right;
Float3::Transform(Float3::Forward, orientation, direction);
Float3::Transform(Float3::Up, orientation, up);
Float3::Transform(Float3::Right, orientation, right);
const Vector3 end = position + direction * (100.0f * scale);
const Vector3 capEnd = position + direction * (70.0f * scale);
const float arrowSidesRatio = scale * 30.0f;
const Vector3 capEnd = end - (direction * (100 * Math::Min(capScale, scale * 0.5f)));
const float arrowSidesRatio = Math::Min(capScale, scale * 0.5f) * 30.0f;
DrawLine(position, end, color, duration, depthTest);
DrawLine(end, capEnd + up * arrowSidesRatio, color, duration, depthTest);

View File

@@ -218,10 +218,11 @@ namespace FlaxEngine
/// <param name="position">The arrow origin position.</param>
/// <param name="orientation">The orientation (defines the arrow direction).</param>
/// <param name="scale">The arrow scale (used to adjust the arrow size).</param>
/// <param name="capScale">The arrow cap scale.</param>
/// <param name="color">The color.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
public static void DrawWireArrow(Vector3 position, Quaternion orientation, float scale, Color color, float duration = 0.0f, bool depthTest = true)
public static void DrawWireArrow(Vector3 position, Quaternion orientation, float scale, float capScale, Color color, float duration = 0.0f, bool depthTest = true)
{
}

View File

@@ -570,10 +570,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, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the box.
@@ -650,7 +651,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
#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_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
@@ -679,7 +680,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CONE(position, orientation, radius, angleXY, angleXZ, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARC(position, orientation, radius, angle, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, capScale, color, duration, depthTest)
#define DEBUG_DRAW_TEXT(text, position, color, size, duration)
#endif

View File

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

View File

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

View File

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

View File

@@ -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);
}
@@ -544,7 +522,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.");
@@ -608,6 +586,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);
@@ -699,6 +678,7 @@ bool GPUTexture::DownloadData(TextureData& result)
{
MISSING_CODE("support volume texture data downloading.");
}
PROFILE_CPU();
// Use faster path for staging resources
if (IsStaging())
@@ -780,6 +760,7 @@ Task* GPUTexture::DownloadDataAsync(TextureData& result)
{
MISSING_CODE("support volume texture data downloading.");
}
PROFILE_CPU();
// Use faster path for staging resources
if (IsStaging())

View File

@@ -269,7 +269,7 @@ 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>
@@ -386,5 +386,9 @@ namespace FlaxEngine
{
return $"{Name} ({GetType().Name})";
}
#if FLAX_EDITOR
internal bool ShowTransform => !(this is UIControl);
#endif
}
}

View File

@@ -534,9 +534,7 @@ public:
/// <summary>
/// Gets actor direction vector (forward vector).
/// </summary>
/// <returns>The result value.</returns>
API_PROPERTY(Attributes="HideInEditor, NoSerialize")
FORCE_INLINE Float3 GetDirection() const
API_PROPERTY(Attributes="HideInEditor, NoSerialize") FORCE_INLINE Float3 GetDirection() const
{
return Float3::Transform(Float3::Forward, GetOrientation());
}
@@ -571,7 +569,7 @@ public:
/// <summary>
/// Gets local position of the actor in parent actor space.
/// </summary>
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Position\"), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorOrder(-30), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+PositionEditor\")")
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Position\"), VisibleIf(\"ShowTransform\"), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorOrder(-30), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+PositionEditor\")")
FORCE_INLINE Vector3 GetLocalPosition() const
{
return _localTransform.Translation;
@@ -587,7 +585,7 @@ public:
/// Gets local rotation of the actor in parent actor space.
/// </summary>
/// <code>Actor.LocalOrientation *= Quaternion.Euler(0, 10 * Time.DeltaTime, 0)</code>
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Rotation\"), DefaultValue(typeof(Quaternion), \"0,0,0,1\"), EditorOrder(-20), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+OrientationEditor\")")
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Rotation\"), VisibleIf(\"ShowTransform\"), DefaultValue(typeof(Quaternion), \"0,0,0,1\"), EditorOrder(-20), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+OrientationEditor\")")
FORCE_INLINE Quaternion GetLocalOrientation() const
{
return _localTransform.Orientation;
@@ -602,7 +600,7 @@ public:
/// <summary>
/// Gets local scale vector of the actor in parent actor space.
/// </summary>
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Scale\"), DefaultValue(typeof(Float3), \"1,1,1\"), Limit(float.MinValue, float.MaxValue, 0.01f), EditorOrder(-10), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+ScaleEditor\")")
API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Scale\"), VisibleIf(\"ShowTransform\"), DefaultValue(typeof(Float3), \"1,1,1\"), Limit(float.MinValue, float.MaxValue, 0.01f), EditorOrder(-10), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+ScaleEditor\")")
FORCE_INLINE Float3 GetLocalScale() const
{
return _localTransform.Scale;

View File

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

View File

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

View File

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

View File

@@ -206,7 +206,7 @@ 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);
@@ -280,12 +280,8 @@ void CharacterController::OnActiveTransformChanged()
// Change actor transform (but with locking)
ASSERT(!_isUpdatingTransform);
_isUpdatingTransform = true;
Transform transform;
PhysicsBackend::GetRigidActorPose(PhysicsBackend::GetShapeActor(_shape), transform.Translation, transform.Orientation);
transform.Translation -= _center;
transform.Orientation = _transform.Orientation;
transform.Scale = _transform.Scale;
SetTransform(transform);
const Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center;
SetPosition(position);
_isUpdatingTransform = false;
UpdateBounds();

View File

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

View File

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

View File

@@ -58,9 +58,7 @@ API_STRUCT(NoDefault) 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>
@@ -87,9 +85,7 @@ public:
/// <summary>
/// Gets the relative linear velocity of the two colliding objects.
/// </summary>
/// <remarks>
/// Can be used to detect stronger collisions.
/// </remarks>
/// <remarks>Can be used to detect stronger collisions. </remarks>
Vector3 GetRelativeVelocity() const
{
return ThisVelocity - OtherVelocity;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,14 +17,14 @@ struct RenderContext;
/// <summary>
/// Represents a single terrain chunk.
/// </summary>
class FLAXENGINE_API TerrainChunk : public ISerializable
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API TerrainChunk : public ScriptingObject, public ISerializable
{
DECLARE_SCRIPTING_TYPE(TerrainChunk);
friend Terrain;
friend TerrainPatch;
friend TerrainChunk;
private:
TerrainPatch* _patch;
uint16 _x, _z;
Float4 _heightmapUVScaleBias;
@@ -41,11 +41,10 @@ private:
void Init(TerrainPatch* patch, uint16 x, uint16 z);
public:
/// <summary>
/// The material to override the terrain default one for this chunk.
/// </summary>
AssetReference<MaterialBase> OverrideMaterial;
API_FIELD() AssetReference<MaterialBase> OverrideMaterial;
/// <summary>
/// The baked lightmap entry info for this chunk.
@@ -53,11 +52,10 @@ public:
LightmapEntry Lightmap;
public:
/// <summary>
/// Gets the x coordinate.
/// </summary>
FORCE_INLINE int32 GetX() const
API_FUNCTION() FORCE_INLINE int32 GetX() const
{
return _x;
}
@@ -65,7 +63,7 @@ public:
/// <summary>
/// Gets the z coordinate.
/// </summary>
FORCE_INLINE int32 GetZ() const
API_FUNCTION() FORCE_INLINE int32 GetZ() const
{
return _z;
}
@@ -73,7 +71,7 @@ public:
/// <summary>
/// Gets the patch.
/// </summary>
FORCE_INLINE TerrainPatch* GetPatch() const
API_FUNCTION() FORCE_INLINE TerrainPatch* GetPatch() const
{
return _patch;
}
@@ -81,7 +79,7 @@ public:
/// <summary>
/// Gets the chunk world bounds.
/// </summary>
FORCE_INLINE const BoundingBox& GetBounds() const
API_FUNCTION() FORCE_INLINE const BoundingBox& GetBounds() const
{
return _bounds;
}
@@ -89,7 +87,7 @@ public:
/// <summary>
/// Gets the chunk transformation (world to local).
/// </summary>
FORCE_INLINE const Transform& GetTransform() const
API_FUNCTION() FORCE_INLINE const Transform& GetTransform() const
{
return _transform;
}
@@ -97,10 +95,9 @@ public:
/// <summary>
/// Gets the scale (in XY) and bias (in ZW) applied to the vertex UVs to get the chunk coordinates.
/// </summary>
/// <param name="result">The result.</param>
FORCE_INLINE void GetHeightmapUVScaleBias(Float4* result) const
API_FUNCTION() FORCE_INLINE const Float4& GetHeightmapUVScaleBias() const
{
*result = _heightmapUVScaleBias;
return _heightmapUVScaleBias;
}
/// <summary>
@@ -120,7 +117,6 @@ public:
}
public:
/// <summary>
/// Prepares for drawing chunk. Cached LOD and material.
/// </summary>
@@ -140,7 +136,7 @@ public:
/// <param name="renderContext">The rendering context.</param>
/// <param name="material">The material to use for rendering.</param>
/// <param name="lodIndex">The LOD index.</param>
void Draw(const RenderContext& renderContext, MaterialBase* material, int32 lodIndex = 0) const;
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, int32 lodIndex = 0) const;
/// <summary>
/// Determines if there is an intersection between the terrain chunk and a point
@@ -148,7 +144,7 @@ public:
/// <param name="ray">The ray.</param>
/// <param name="distance">The output distance.</param>
/// <returns>True if chunk intersects with the ray, otherwise false.</returns>
bool Intersects(const Ray& ray, Real& distance);
API_FUNCTION() bool Intersects(const Ray& ray, API_PARAM(Out) Real& distance);
/// <summary>
/// Updates the cached bounds of the chunk.
@@ -166,7 +162,6 @@ public:
void CacheNeighbors();
public:
// [ISerializable]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;

File diff suppressed because it is too large Load Diff

View File

@@ -15,22 +15,14 @@ class TerrainMaterialShader;
/// <summary>
/// Represents single terrain patch made of 16 terrain chunks.
/// </summary>
class FLAXENGINE_API TerrainPatch : public ISerializable
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API TerrainPatch : public ScriptingObject, public ISerializable
{
DECLARE_SCRIPTING_TYPE(TerrainPatch);
friend Terrain;
friend TerrainPatch;
friend TerrainChunk;
public:
enum
{
CHUNKS_COUNT = 16,
CHUNKS_COUNT_EDGE = 4,
};
private:
Terrain* _terrain;
int16 _x, _z;
float _yOffset, _yHeight;
@@ -52,7 +44,7 @@ private:
TextureBase::InitData* _dataSplatmap[TERRAIN_MAX_SPLATMAPS_COUNT] = {};
#endif
#if TERRAIN_USE_PHYSICS_DEBUG
Array<Vector3> _debugLines; // TODO: large-worlds
Array<Vector3> _debugLines; // TODO: large-worlds
#endif
#if USE_EDITOR
Array<Vector3> _collisionTriangles; // TODO: large-worlds
@@ -62,23 +54,21 @@ private:
void Init(Terrain* terrain, int16 x, int16 z);
public:
/// <summary>
/// Finalizes an instance of the <see cref="TerrainPatch"/> class.
/// </summary>
~TerrainPatch();
public:
/// <summary>
/// The chunks contained within the patch. Organized in 4x4 square.
/// </summary>
TerrainChunk Chunks[CHUNKS_COUNT];
TerrainChunk Chunks[Terrain::ChunksCount];
/// <summary>
/// The heightmap texture.
/// </summary>
AssetReference<Texture> Heightmap;
API_FIELD() AssetReference<Texture> Heightmap;
/// <summary>
/// The splatmap textures.
@@ -86,12 +76,10 @@ public:
AssetReference<Texture> Splatmap[TERRAIN_MAX_SPLATMAPS_COUNT];
public:
/// <summary>
/// Gets the Y axis heightmap offset from terrain origin.
/// </summary>
/// <returns>The offset.</returns>
FORCE_INLINE float GetOffsetY() const
API_FUNCTION() FORCE_INLINE float GetOffsetY() const
{
return _yOffset;
}
@@ -99,8 +87,7 @@ public:
/// <summary>
/// Gets the Y axis heightmap height.
/// </summary>
/// <returns>The height.</returns>
FORCE_INLINE float GetHeightY() const
API_FUNCTION() FORCE_INLINE float GetHeightY() const
{
return _yHeight;
}
@@ -108,8 +95,7 @@ public:
/// <summary>
/// Gets the x coordinate.
/// </summary>
/// <returns>The x position.</returns>
FORCE_INLINE int32 GetX() const
API_FUNCTION() FORCE_INLINE int32 GetX() const
{
return _x;
}
@@ -117,8 +103,7 @@ public:
/// <summary>
/// Gets the z coordinate.
/// </summary>
/// <returns>The z position.</returns>
FORCE_INLINE int32 GetZ() const
API_FUNCTION() FORCE_INLINE int32 GetZ() const
{
return _z;
}
@@ -126,8 +111,7 @@ public:
/// <summary>
/// Gets the terrain.
/// </summary>
/// <returns>The terrain,</returns>
FORCE_INLINE Terrain* GetTerrain() const
API_FUNCTION() FORCE_INLINE Terrain* GetTerrain() const
{
return _terrain;
}
@@ -137,9 +121,9 @@ public:
/// </summary>
/// <param name="index">The chunk zero-based index.</param>
/// <returns>The chunk.</returns>
TerrainChunk* GetChunk(int32 index)
API_FUNCTION() TerrainChunk* GetChunk(int32 index)
{
if (index < 0 || index >= CHUNKS_COUNT)
if (index < 0 || index >= Terrain::ChunksCount)
return nullptr;
return &Chunks[index];
}
@@ -149,9 +133,9 @@ public:
/// </summary>
/// <param name="chunkCoord">The chunk location (x and z).</param>
/// <returns>The chunk.</returns>
TerrainChunk* GetChunk(const Int2& chunkCoord)
API_FUNCTION() TerrainChunk* GetChunk(API_PARAM(Ref) const Int2& chunkCoord)
{
return GetChunk(chunkCoord.Y * CHUNKS_COUNT_EDGE + chunkCoord.X);
return GetChunk(chunkCoord.Y * Terrain::ChunksCountEdge + chunkCoord.X);
}
/// <summary>
@@ -160,22 +144,43 @@ public:
/// <param name="x">The chunk location x.</param>
/// <param name="z">The chunk location z.</param>
/// <returns>The chunk.</returns>
TerrainChunk* GetChunk(int32 x, int32 z)
API_FUNCTION() TerrainChunk* GetChunk(int32 x, int32 z)
{
return GetChunk(z * CHUNKS_COUNT_EDGE + x);
return GetChunk(z * Terrain::ChunksCountEdge + x);
}
/// <summary>
/// Gets the splatmap assigned to this patch.
/// </summary>
/// <param name="index">The zero-based index of the splatmap.</param>
/// <returns>The splatmap texture.</returns>
API_FUNCTION() AssetReference<Texture> GetSplatmap(int32 index)
{
if (index < 0 || index >= TERRAIN_MAX_SPLATMAPS_COUNT)
return nullptr;
return Splatmap[index];
}
/// <summary>
/// Sets a splatmap to this patch.
/// </summary>
/// <param name="index">The zero-based index of the splatmap.</param>
/// <param name="splatMap">Splatmap texture.</param>
API_FUNCTION() void SetSplatmap(int32 index, const AssetReference<Texture>& splatMap)
{
if (index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT)
Splatmap[index] = splatMap;
}
/// <summary>
/// Gets the patch world bounds.
/// </summary>
/// <returns>The bounding box.</returns>
FORCE_INLINE const BoundingBox& GetBounds() const
API_FUNCTION() FORCE_INLINE const BoundingBox& GetBounds() const
{
return _bounds;
}
public:
/// <summary>
/// Removes the lightmap data from the terrain patch.
/// </summary>
@@ -192,12 +197,11 @@ public:
void UpdateTransform();
#if TERRAIN_EDITING
/// <summary>
/// Initializes the patch heightmap and collision to the default flat level.
/// </summary>
/// <returns>True if failed, otherwise false.</returns>
bool InitializeHeightMap();
API_FUNCTION() bool InitializeHeightMap();
/// <summary>
/// Setups the terrain patch using the specified heightmap data.
@@ -207,7 +211,7 @@ public:
/// <param name="holesMask">The holes mask (optional). Normalized to 0-1 range values with holes mask per-vertex. Must match the heightmap dimensions.</param>
/// <param name="forceUseVirtualStorage">If set to <c>true</c> patch will use virtual storage by force. Otherwise it can use normal texture asset storage on drive (valid only during Editor). Runtime-created terrain can only use virtual storage (in RAM).</param>
/// <returns>True if failed, otherwise false.</returns>
bool SetupHeightMap(int32 heightMapLength, const float* heightMap, const byte* holesMask = nullptr, bool forceUseVirtualStorage = false);
API_FUNCTION() bool SetupHeightMap(int32 heightMapLength, API_PARAM(Ref) const float* heightMap, API_PARAM(Ref) const byte* holesMask = nullptr, bool forceUseVirtualStorage = false);
/// <summary>
/// Setups the terrain patch layer weights using the specified splatmaps data.
@@ -217,50 +221,48 @@ public:
/// <param name="splatMap">The splat map. Each array item contains 4 layer weights.</param>
/// <param name="forceUseVirtualStorage">If set to <c>true</c> patch will use virtual storage by force. Otherwise it can use normal texture asset storage on drive (valid only during Editor). Runtime-created terrain can only use virtual storage (in RAM).</param>
/// <returns>True if failed, otherwise false.</returns>
bool SetupSplatMap(int32 index, int32 splatMapLength, const Color32* splatMap, bool forceUseVirtualStorage = false);
API_FUNCTION() bool SetupSplatMap(int32 index, int32 splatMapLength, API_PARAM(Ref) const Color32* splatMap, bool forceUseVirtualStorage = false);
#endif
#if TERRAIN_UPDATING
/// <summary>
/// Gets the raw pointer to the heightmap data.
/// </summary>
/// <returns>The heightmap data.</returns>
float* GetHeightmapData();
API_FUNCTION() float* GetHeightmapData();
/// <summary>
/// Clears cache of the heightmap data.
/// </summary>
void ClearHeightmapCache();
API_FUNCTION() void ClearHeightmapCache();
/// <summary>
/// Gets the raw pointer to the holes mask data.
/// </summary>
/// <returns>The holes mask data.</returns>
byte* GetHolesMaskData();
API_FUNCTION() byte* GetHolesMaskData();
/// <summary>
/// Clears cache of the holes mask data.
/// </summary>
void ClearHolesMaskCache();
API_FUNCTION() void ClearHolesMaskCache();
/// <summary>
/// Gets the raw pointer to the splat map data.
/// </summary>
/// <param name="index">The zero-based index of the splatmap texture.</param>
/// <returns>The splat map data.</returns>
Color32* GetSplatMapData(int32 index);
API_FUNCTION() Color32* GetSplatMapData(int32 index);
/// <summary>
/// Clears cache of the splat map data.
/// </summary>
void ClearSplatMapCache();
API_FUNCTION() void ClearSplatMapCache();
/// <summary>
/// Clears all caches.
/// </summary>
void ClearCache();
API_FUNCTION() void ClearCache();
/// <summary>
/// Modifies the terrain patch heightmap with the given samples.
@@ -269,7 +271,7 @@ public:
/// <param name="modifiedOffset">The offset from the first row and column of the heightmap data (offset destination x and z start position).</param>
/// <param name="modifiedSize">The size of the heightmap to modify (x and z). Amount of samples in each direction.</param>
/// <returns>True if failed, otherwise false.</returns>
bool ModifyHeightMap(const float* samples, const Int2& modifiedOffset, const Int2& modifiedSize);
API_FUNCTION() bool ModifyHeightMap(API_PARAM(Ref) const float* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize);
/// <summary>
/// Modifies the terrain patch holes mask with the given samples.
@@ -278,7 +280,7 @@ public:
/// <param name="modifiedOffset">The offset from the first row and column of the holes map data (offset destination x and z start position).</param>
/// <param name="modifiedSize">The size of the holes map to modify (x and z). Amount of samples in each direction.</param>
/// <returns>True if failed, otherwise false.</returns>
bool ModifyHolesMask(const byte* samples, const Int2& modifiedOffset, const Int2& modifiedSize);
API_FUNCTION() bool ModifyHolesMask(API_PARAM(Ref) const byte* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize);
/// <summary>
/// Modifies the terrain patch splat map (layers mask) with the given samples.
@@ -288,21 +290,18 @@ public:
/// <param name="modifiedOffset">The offset from the first row and column of the splat map data (offset destination x and z start position).</param>
/// <param name="modifiedSize">The size of the splat map to modify (x and z). Amount of samples in each direction.</param>
/// <returns>True if failed, otherwise false.</returns>
bool ModifySplatMap(int32 index, const Color32* samples, const Int2& modifiedOffset, const Int2& modifiedSize);
API_FUNCTION() bool ModifySplatMap(int32 index, API_PARAM(Ref) const Color32* samples, API_PARAM(Ref) const Int2& modifiedOffset, API_PARAM(Ref) const Int2& modifiedSize);
private:
bool UpdateHeightData(const struct TerrainDataUpdateInfo& info, const Int2& modifiedOffset, const Int2& modifiedSize, bool wasHeightRangeChanged);
bool UpdateHeightData(struct TerrainDataUpdateInfo& info, const Int2& modifiedOffset, const Int2& modifiedSize, bool wasHeightRangeChanged, bool wasHeightChanged);
void SaveHeightData();
void CacheHeightData();
void SaveSplatData();
void SaveSplatData(int32 index);
void CacheSplatData();
#endif
public:
/// <summary>
/// Performs a raycast against this terrain collision shape.
/// </summary>
@@ -311,7 +310,7 @@ public:
/// <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>
bool RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance = MAX_float) const;
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 terrain collision shape.
@@ -322,7 +321,7 @@ public:
/// <param name="resultHitNormal">The raycast result hit position normal vector. 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, Vector3& resultHitNormal, float maxDistance = MAX_float) const;
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) float& resultHitDistance, API_PARAM(Out) Vector3& resultHitNormal, float maxDistance = MAX_float) const;
/// <summary>
/// Performs a raycast against this terrain collision shape. Returns the hit chunk.
@@ -333,7 +332,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 terrain collision, returns results in a RaycastHit structure.
@@ -343,28 +342,24 @@ public:
/// <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>
bool RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance = MAX_float) const;
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>
void ClosestPoint(const Vector3& position, Vector3& result) const;
API_FUNCTION() void ClosestPoint(API_PARAM(Ref) const Vector3& position, API_PARAM(Out) Vector3& result) const;
#if USE_EDITOR
/// <summary>
/// Updates the patch data after manual deserialization called at runtime (eg. by editor undo).
/// </summary>
void UpdatePostManualDeserialization();
#endif
public:
#if USE_EDITOR
/// <summary>
/// Gets the collision mesh triangles array (3 vertices per triangle in linear list). Cached internally to reuse data.
/// </summary>
@@ -376,8 +371,7 @@ public:
/// </summary>
/// <param name="bounds">The world-space bounds to find terrain triangles that intersect with it.</param>
/// <param name="result">The result triangles that intersect with the given bounds (in world-space).</param>
void GetCollisionTriangles(const BoundingSphere& bounds, Array<Vector3>& result);
void GetCollisionTriangles(API_PARAM(Ref) const BoundingSphere& bounds, API_PARAM(Out) Array<Vector3>& result);
#endif
/// <summary>
@@ -385,10 +379,9 @@ public:
/// </summary>
/// <param name="vertexBuffer">The output vertex buffer.</param>
/// <param name="indexBuffer">The output index buffer.</param>
void ExtractCollisionGeometry(Array<Float3>& vertexBuffer, Array<int32>& indexBuffer);
API_FUNCTION() void ExtractCollisionGeometry(API_PARAM(Out) Array<Float3>& vertexBuffer, API_PARAM(Out) Array<int32>& indexBuffer);
private:
/// <summary>
/// Determines whether this patch has created collision representation.
/// </summary>
@@ -419,8 +412,8 @@ private:
void DestroyCollision();
#if TERRAIN_USE_PHYSICS_DEBUG
void CacheDebugLines();
void DrawPhysicsDebug(RenderView& view);
void CacheDebugLines();
void DrawPhysicsDebug(RenderView& view);
#endif
/// <summary>
@@ -430,8 +423,8 @@ private:
bool UpdateCollision();
void OnPhysicsSceneChanged(PhysicsScene* previous);
public:
public:
// [ISerializable]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;

View File

@@ -1468,7 +1468,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
Transform srcNode = data.Skeleton.Nodes[nodeIndex].LocalTransform;
auto& node = data.Skeleton.Nodes[nodeIndex];
if (auto* channel = animation.GetChannel(node.Name))
channel->Evaluate(frame, &srcNode, false);
channel->Evaluate((float)frame, &srcNode, false);
pose.Nodes[nodeIndex] = srcNode;
}
@@ -1476,7 +1476,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
key = Float3::Zero;
for (int32 nodeIndex = 0; nodeIndex < nodes; nodeIndex++)
key += pose.GetNodeModelTransformation(data.Skeleton, nodeIndex).Translation;
key /= nodes;
key /= (float)nodes;
}
// Calculate skeleton center of mass movement over the animation frames
@@ -1485,7 +1485,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
for (int32 frame = 0; frame < frames; frame++)
{
auto& key = rootChannel.Position[frame];
key.Time = frame;
key.Time = (float)frame;
key.Value = centerOfMass[frame] - centerOfMassRefPose;
}
@@ -1531,7 +1531,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
Transform srcNode = data.Skeleton.Nodes[nodeIndex].LocalTransform;
auto& node = data.Skeleton.Nodes[nodeIndex];
if (auto* channel = animation.GetChannel(node.Name))
channel->Evaluate(frame, &srcNode, false);
channel->Evaluate((float)frame, &srcNode, false);
pose.Nodes[nodeIndex] = srcNode;
}

View File

@@ -1,4 +1,6 @@
using System;
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
namespace FlaxEngine.GUI;
@@ -7,6 +9,32 @@ namespace FlaxEngine.GUI;
/// </summary>
public class Slider : ContainerControl
{
/// <summary>
/// The slider direction
/// </summary>
public enum SliderDirection
{
/// <summary>
/// Slider direction, horizontal right
/// </summary>
HorizontalRight,
/// <summary>
/// Slider direction, horizontal left
/// </summary>
HorizontalLeft,
/// <summary>
/// Slider direction, vertical up
/// </summary>
VerticalUp,
/// <summary>
/// Slider direction, vertical down
/// </summary>
VerticalDown,
}
/// <summary>
/// The minimum value.
/// </summary>
@@ -15,26 +43,7 @@ public class Slider : ContainerControl
/// <summary>
/// The maximum value.
/// </summary>
protected float _maximum = 100f;
/// <summary>
/// Gets or sets the minimum value.
/// </summary>
[EditorOrder(20), Tooltip("The minimum value.")]
public float Minimum
{
get => _minimum;
set
{
if (value > _maximum)
throw new ArgumentOutOfRangeException();
if (WholeNumbers)
value = Mathf.RoundToInt(value);
_minimum = value;
if (Value < _minimum)
Value = _minimum;
}
}
protected float _maximum = 100;
/// <summary>
/// Gets or sets the maximum value.
@@ -45,8 +54,6 @@ public class Slider : ContainerControl
get => _maximum;
set
{
if (value < _minimum || Mathf.IsZero(value))
throw new ArgumentOutOfRangeException();
if (WholeNumbers)
value = Mathf.RoundToInt(value);
_maximum = value;
@@ -55,6 +62,38 @@ public class Slider : ContainerControl
}
}
/// <summary>
/// Gets or sets the minimum value.
/// </summary>
[EditorOrder(20), Tooltip("The minimum value.")]
public float Minimum
{
get => _minimum;
set
{
if (WholeNumbers)
value = Mathf.RoundToInt(value);
_minimum = value;
if (Value < _minimum)
Value = _minimum;
}
}
/// <summary>
/// Gets or sets the slider direction.
/// </summary>
[EditorOrder(40), Tooltip("Slider Direction.")]
public SliderDirection Direction
{
get => _direction;
set
{
_direction = value;
UpdateThumb();
}
}
private SliderDirection _direction = SliderDirection.HorizontalRight;
private float _value = 100f;
private Rectangle _thumbRect;
private float _thumbCenter;
@@ -89,31 +128,60 @@ public class Slider : ContainerControl
/// The local position of the thumb center
/// </summary>
[HideInEditor]
public Float2 ThumbCenter => new(_thumbCenter, Height / 2);
public Float2 ThumbCenter => (Direction is SliderDirection.HorizontalLeft or SliderDirection.HorizontalRight) ? new Float2(_thumbCenter, Height / 2) : new Float2(Width / 2, _thumbCenter);
/// <summary>
/// The local position of the beginning of the track.
/// </summary>
[HideInEditor]
public Float2 TrackBeginning => new(_thumbSize.X / 2, Height / 2);
public Float2 TrackBeginning
{
get
{
switch (Direction)
{
case SliderDirection.HorizontalRight: return new Float2(_thumbSize.X / 2, Height / 2);
case SliderDirection.HorizontalLeft: return new Float2(Width - _thumbSize.X / 2, Height / 2);
case SliderDirection.VerticalUp: return new Float2(Width / 2, Height - _thumbSize.Y / 2);
case SliderDirection.VerticalDown: return new Float2(Width / 2, _thumbSize.Y / 2);
default: break;
}
return Float2.Zero;
}
}
/// <summary>
/// The local position of the end of the track.
/// </summary>
[HideInEditor]
public Float2 TrackEnd => new(Width - _thumbSize.X / 2, Height / 2);
public Float2 TrackEnd
{
get
{
switch (Direction)
{
case SliderDirection.HorizontalRight: return new Float2(Width - _thumbSize.X / 2, Height / 2);
case SliderDirection.HorizontalLeft: return new Float2(_thumbSize.X / 2, Height / 2);
case SliderDirection.VerticalUp: return new Float2(Width / 2, _thumbSize.Y / 2);
case SliderDirection.VerticalDown: return new Float2(Width / 2, Height - _thumbSize.Y / 2);
default: break;
}
return Float2.Zero;
}
}
/// <summary>
/// The height of the track.
/// </summary>
[EditorOrder(40), Tooltip("The track height.")]
public int TrackHeight { get; set; } = 2;
public int TrackThickness { get; set; } = 2;
/// <summary>
/// The thumb size.
/// </summary>
[EditorOrder(41), Tooltip("The size of the thumb.")]
public Float2 ThumbSize {
public Float2 ThumbSize
{
get => _thumbSize;
set
{
@@ -127,7 +195,7 @@ public class Slider : ContainerControl
/// </summary>
[EditorOrder(42), Tooltip("Fill the track.")]
public bool FillTrack = true;
/// <summary>
/// Whether to use whole numbers.
/// </summary>
@@ -147,9 +215,14 @@ public class Slider : ContainerControl
public Color TrackFillLineColor { get; set; }
/// <summary>
/// Gets the size of the track.
/// Gets the width of the track.
/// </summary>
private float TrackWidth => Width;
private float TrackWidth => (Direction is SliderDirection.HorizontalLeft or SliderDirection.HorizontalRight) ? Width - _thumbSize.X : TrackThickness;
/// <summary>
/// Gets the height of the track.
/// </summary>
private float TrackHeight => (Direction is SliderDirection.HorizontalLeft or SliderDirection.HorizontalRight) ? TrackThickness : Height - _thumbSize.Y;
/// <summary>
/// Gets or sets the brush used for slider track drawing.
@@ -168,7 +241,7 @@ public class Slider : ContainerControl
/// </summary>
[EditorDisplay("Thumb Style"), EditorOrder(2030), Tooltip("The color of the slider thumb when it's not selected."), ExpandGroups]
public Color ThumbColor { get; set; }
/// <summary>
/// The color of the slider thumb when it's highlighted.
/// </summary>
@@ -202,12 +275,12 @@ public class Slider : ContainerControl
/// Occurs when sliding ends.
/// </summary>
public event Action SlidingEnd;
/// <summary>
/// Occurs when value gets changed.
/// </summary>
public event Action ValueChanged;
/// <summary>
/// Initializes a new instance of the <see cref="Slider"/> class.
/// </summary>
@@ -236,13 +309,32 @@ public class Slider : ContainerControl
private void UpdateThumb()
{
// Cache data
float trackSize = TrackWidth;
var isHorizontal = Direction is SliderDirection.HorizontalRight or SliderDirection.HorizontalLeft;
float trackSize = isHorizontal ? Width : Height;
float range = Maximum - Minimum;
float pixelRange = trackSize - _thumbSize.X;
float pixelRange = trackSize - (isHorizontal ? _thumbSize.X : _thumbSize.Y);
float perc = (_value - Minimum) / range;
float thumbPosition = (int)(perc * pixelRange);
_thumbCenter = thumbPosition + _thumbSize.X / 2;
_thumbRect = new Rectangle(thumbPosition, (Height - _thumbSize.Y) / 2, _thumbSize.X, _thumbSize.Y);
switch (Direction)
{
case SliderDirection.HorizontalRight:
_thumbCenter = thumbPosition + _thumbSize.X / 2;
_thumbRect = new Rectangle(thumbPosition, (Height - _thumbSize.Y) / 2, _thumbSize.X, _thumbSize.Y);
break;
case SliderDirection.VerticalDown:
_thumbCenter = thumbPosition + _thumbSize.Y / 2;
_thumbRect = new Rectangle((Width - _thumbSize.X) / 2, thumbPosition, _thumbSize.X, _thumbSize.Y);
break;
case SliderDirection.HorizontalLeft:
_thumbCenter = Width - thumbPosition - _thumbSize.X / 2;
_thumbRect = new Rectangle(Width - thumbPosition - _thumbSize.X, (Height - _thumbSize.Y) / 2, _thumbSize.X, _thumbSize.Y);
break;
case SliderDirection.VerticalUp:
_thumbCenter = Height - thumbPosition - _thumbSize.Y / 2;
_thumbRect = new Rectangle((Width - _thumbSize.X) / 2, Height - thumbPosition - _thumbSize.Y, _thumbSize.X, _thumbSize.Y);
break;
default: break;
}
}
private void EndSliding()
@@ -256,19 +348,36 @@ public class Slider : ContainerControl
public override void Draw()
{
base.Draw();
// Set rectangles
var lineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackThickness) / 2, Width - _thumbSize.X, TrackThickness);
var fillLineRect = new Rectangle(_thumbSize.X / 2 - 1, (Height - TrackThickness - 2) / 2, Width - (Width - _thumbCenter) - _thumbSize.X / 2 + 1, TrackThickness + 2);
switch (Direction)
{
case SliderDirection.HorizontalRight: break;
case SliderDirection.VerticalDown:
lineRect = new Rectangle((Width - TrackThickness) / 2, _thumbSize.Y / 2, TrackThickness, Height - _thumbSize.Y);
fillLineRect = new Rectangle((Width - TrackThickness - 2) / 2, _thumbSize.Y / 2 - 1, TrackThickness + 2, Height - (Height - _thumbCenter) - _thumbSize.Y / 2 + 1);
break;
case SliderDirection.HorizontalLeft:
fillLineRect = new Rectangle(Width - (Width - _thumbCenter) - 1, (Height - TrackThickness - 2) / 2, Width - _thumbCenter + 1, TrackThickness + 2);
break;
case SliderDirection.VerticalUp:
lineRect = new Rectangle((Width - TrackThickness) / 2, _thumbSize.Y / 2, TrackThickness, Height - _thumbSize.Y);
fillLineRect = new Rectangle((Width - TrackThickness - 2) / 2, Height - (Height - _thumbCenter) - 1, TrackThickness + 2, Height - _thumbCenter + 1);
break;
default: break;
}
// Draw track line
//var lineRect = new Rectangle(4, (Height - TrackHeight) / 2, Width - 8, TrackHeight);
var lineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackHeight) / 2, Width - _thumbSize.X, TrackHeight);
if (TrackBrush != null)
TrackBrush.Draw(lineRect, TrackLineColor);
else
Render2D.FillRectangle(lineRect, TrackLineColor);
// Draw track fill
if (FillTrack)
{
var fillLineRect = new Rectangle(_thumbSize.X / 2 - 1, (Height - TrackHeight - 2) / 2, Width - (Width - _thumbCenter) - _thumbSize.X / 2, TrackHeight + 2);
Render2D.PushClip(ref fillLineRect);
if (FillTrackBrush != null)
FillTrackBrush.Draw(lineRect, TrackFillLineColor);
@@ -276,13 +385,13 @@ public class Slider : ContainerControl
Render2D.FillRectangle(lineRect, TrackFillLineColor);
Render2D.PopClip();
}
// Draw thumb
var thumbColor = _isSliding ? ThumbColorSelected : (_mouseOverThumb ? ThumbColorHighlighted : ThumbColor);
var thumbColorV = _isSliding ? ThumbColorSelected : (_mouseOverThumb ? ThumbColorHighlighted : ThumbColor);
if (ThumbBrush != null)
ThumbBrush.Draw(_thumbRect, thumbColor);
ThumbBrush.Draw(_thumbRect, thumbColorV);
else
Render2D.FillRectangle(_thumbRect, thumbColor);
Render2D.FillRectangle(_thumbRect, thumbColorV);
}
/// <inheritdoc />
@@ -302,7 +411,7 @@ public class Slider : ContainerControl
if (button == MouseButton.Left)
{
Focus();
float mousePosition = location.X;
float mousePosition = Direction is SliderDirection.HorizontalRight or SliderDirection.HorizontalLeft ? location.X : location.Y;
if (_thumbRect.Contains(ref location))
{
@@ -315,7 +424,16 @@ public class Slider : ContainerControl
else
{
// Click change
Value += (mousePosition < _thumbCenter ? -1 : 1) * 10;
switch (Direction)
{
case SliderDirection.HorizontalRight or SliderDirection.VerticalDown:
Value += (mousePosition < _thumbCenter ? -1 : 1) * 10;
break;
case SliderDirection.HorizontalLeft or SliderDirection.VerticalUp:
Value -= (mousePosition < _thumbCenter ? -1 : 1) * 10;
break;
default: break;
}
}
}
@@ -330,7 +448,22 @@ public class Slider : ContainerControl
{
// Update sliding
var slidePosition = location + Root.TrackingMouseOffset;
Value = Mathf.Remap(slidePosition.X, 4, TrackWidth - 4, Minimum, Maximum);
switch (Direction)
{
case SliderDirection.HorizontalRight:
Value = Mathf.Remap(slidePosition.X, 4, Width - 4, Minimum, Maximum);
break;
case SliderDirection.VerticalDown:
Value = Mathf.Remap(slidePosition.Y, 4, Height - 4, Minimum, Maximum);
break;
case SliderDirection.HorizontalLeft:
Value = Mathf.Remap(slidePosition.X, Width - 4, 4, Minimum, Maximum);
break;
case SliderDirection.VerticalUp:
Value = Mathf.Remap(slidePosition.Y, Height - 4, 4, Minimum, Maximum);
break;
default: break;
}
}
else
{

View File

@@ -101,7 +101,7 @@ public:
/// Visject graph parameter.
/// </summary>
/// <seealso cref="GraphParameter" />
API_CLASS() class VisjectGraphParameter : public GraphParameter
API_CLASS() class FLAXENGINE_API VisjectGraphParameter : public GraphParameter
{
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(VisjectGraphParameter, GraphParameter);
public:

View File

@@ -5,10 +5,6 @@
#include "Engine/Serialization/ReadStream.h"
#include "Engine/Serialization/WriteStream.h"
VisjectMeta::VisjectMeta()
{
}
bool VisjectMeta::Load(ReadStream* stream, bool loadData)
{
Release();

View File

@@ -8,7 +8,7 @@
/// <summary>
/// Visject metadata container
/// </summary>
class VisjectMeta
class FLAXENGINE_API VisjectMeta
{
public:
/// <summary>
@@ -27,19 +27,6 @@ public:
/// </summary>
Array<Entry, FixedAllocation<8>> Entries;
public:
/// <summary>
/// Initializes a new instance of the <see cref="VisjectMeta"/> class.
/// </summary>
VisjectMeta();
/// <summary>
/// Finalizes an instance of the <see cref="VisjectMeta"/> class.
/// </summary>
~VisjectMeta()
{
}
public:
/// <summary>
/// Load from the stream