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

This commit is contained in:
2025-06-16 14:09:29 +03:00
65 changed files with 3633 additions and 880 deletions

View File

@@ -9,6 +9,10 @@
#include "Engine/Level/Types.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#if USE_EDITOR
#include "Engine/Core/Collections/HashSet.h"
#include "Engine/Core/Collections/Dictionary.h"
#endif
#include <ThirdParty/rapidjson/document.h>
bool JsonStorageProxy::IsValidExtension(const StringView& extension)
@@ -56,27 +60,31 @@ bool JsonStorageProxy::GetAssetInfo(const StringView& path, Guid& resultId, Stri
#if USE_EDITOR
void ChangeIds(rapidjson_flax::Value& obj, rapidjson_flax::Document& document, const StringAnsi& srcId, const StringAnsi& dstId)
void FindObjectIds(const rapidjson_flax::Value& obj, const rapidjson_flax::Document& document, HashSet<Guid>& ids, const char* parentName = nullptr)
{
if (obj.IsObject())
{
for (rapidjson_flax::Value::MemberIterator i = obj.MemberBegin(); i != obj.MemberEnd(); ++i)
for (rapidjson_flax::Value::ConstMemberIterator i = obj.MemberBegin(); i != obj.MemberEnd(); ++i)
{
ChangeIds(i->value, document, srcId, dstId);
FindObjectIds(i->value, document, ids, i->name.GetString());
}
}
else if (obj.IsArray())
{
for (rapidjson::SizeType i = 0; i < obj.Size(); i++)
{
ChangeIds(obj[i], document, srcId, dstId);
FindObjectIds(obj[i], document, ids, parentName);
}
}
else if (obj.IsString())
else if (obj.IsString() && obj.GetStringLength() == 32)
{
if (StringUtils::Compare(srcId.Get(), obj.GetString()) == 0)
if (parentName && StringUtils::Compare(parentName, "ID") == 0)
{
obj.SetString(dstId.Get(), document.GetAllocator());
auto value = JsonTools::GetGuid(obj);
if (value.IsValid())
{
ids.Add(value);
}
}
}
}
@@ -91,9 +99,7 @@ bool JsonStorageProxy::ChangeId(const StringView& path, const Guid& newId)
// Load file
Array<byte> fileData;
if (File::ReadAllBytes(path, fileData))
{
return false;
}
// Parse data
rapidjson_flax::Document document;
@@ -107,33 +113,35 @@ bool JsonStorageProxy::ChangeId(const StringView& path, const Guid& newId)
return false;
}
// Try get asset metadata
// Get all IDs inside the file
HashSet<Guid> ids;
FindObjectIds(document, document, ids);
// Remap into a unique IDs
Dictionary<Guid, Guid> remap;
remap.EnsureCapacity(ids.Count());
for (const auto& id : ids)
remap.Add(id.Item, Guid::New());
// Remap asset ID using the provided value
auto idNode = document.FindMember("ID");
if (idNode == document.MemberEnd())
{
return true;
}
remap[JsonTools::GetGuid(idNode->value)] = newId;
// Change IDs
auto oldIdStr = idNode->value.GetString();
auto newIdStr = newId.ToString(Guid::FormatType::N).ToStringAnsi();
ChangeIds(document, document, oldIdStr, newIdStr);
// Change IDs of asset and objects inside asset
JsonTools::ChangeIds(document, remap);
// Save to file
rapidjson_flax::StringBuffer buffer;
PrettyJsonWriter writer(buffer);
document.Accept(writer.GetWriter());
if (File::WriteAllBytes(path, (byte*)buffer.GetString(), (int32)buffer.GetSize()))
{
return true;
}
return false;
#else
LOG(Warning, "Editing cooked content is invalid.");
return true;
#endif
}

View File

@@ -18,6 +18,7 @@
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Engine/Platform/MessageBox.h"
bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options)
{
@@ -118,6 +119,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
}
#else
#define HANDLE_VORBIS(chunkIndex, dataPtr, dataSize) \
MessageBox::Show(TEXT("Vorbis format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "Vorbis format is not supported."); \
return CreateAssetResult::Error;
#endif
@@ -140,6 +142,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
break; \
default: \
{ \
MessageBox::Show(TEXT("Unknown audio format."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning); \
LOG(Warning, "Unknown audio format."); \
return CreateAssetResult::Error; \
} \

View File

@@ -12,6 +12,7 @@
#include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Platform/MessageBox.h"
#define USE_MIKKTSPACE 1
#include "ThirdParty/MikkTSpace/mikktspace.h"
#if USE_ASSIMP
@@ -181,6 +182,7 @@ bool MeshData::GenerateLightmapUVs()
for (int32 i = 0; i < (int32)vb.size(); i++)
lightmapChannel.Get()[i] = *(Float2*)&vb[i].uv;
#else
MessageBox::Show(TEXT("Model lightmap UVs generation is not supported on this platform."), TEXT("Import error"), MessageBoxButtons::OK, MessageBoxIcon::Error);
LOG(Error, "Model lightmap UVs generation is not supported on this platform.");
#endif

View File

@@ -110,7 +110,7 @@ public:
/// <summary>
/// The animation update delta timescale. Can be used to speed up animation playback or create slow motion effect.
/// </summary>
API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
float UpdateSpeed = 1.0f;
/// <summary>
@@ -122,7 +122,7 @@ public:
/// <summary>
/// The master scale parameter for the actor bounding box. Helps to reduce mesh flickering effect on screen edges.
/// </summary>
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
float BoundsScale = 1.5f;
/// <summary>

View File

@@ -440,4 +440,18 @@ bool ParticleEmitter::Save(const StringView& path)
return SaveSurface(data);
}
bool ParticleEmitter::HasShaderCode() const
{
if (SimulationMode != ParticlesSimulationMode::GPU)
return false;
#if COMPILE_WITH_PARTICLE_GPU_GRAPH && COMPILE_WITH_SHADER_COMPILER
if (_shaderHeader.ParticleEmitter.GraphVersion == PARTICLE_GPU_GRAPH_VERSION
&& HasChunk(SHADER_FILE_CHUNK_SOURCE)
&& !HasDependenciesModified())
return true;
#endif
return false;
}
#endif

View File

@@ -173,6 +173,11 @@ public:
#if USE_EDITOR
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
bool Save(const StringView& path = StringView::Empty) override;
/// <summary>
/// Checks if the particle emitter has valid shader code present.
/// </summary>
API_PROPERTY() bool HasShaderCode() const;
#endif
protected:

View File

@@ -162,7 +162,7 @@ void BoxCollider::UpdateBounds()
void BoxCollider::GetGeometry(CollisionShape& collision)
{
Float3 size = _size * _cachedScale;
Float3 size = _size * _transform.Scale;
const float minSize = 0.001f;
size = Float3::Max(size.GetAbsolute() * 0.5f, Float3(minSize));
collision.SetBox(size.Raw);

View File

@@ -43,10 +43,9 @@ void CapsuleCollider::DrawPhysicsDebug(RenderView& view)
return;
Quaternion rotation;
Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rotation);
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize);
const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize);
if (view.Mode == ViewMode::PhysicsColliders && !GetIsTrigger())
DEBUG_DRAW_CAPSULE(_transform.LocalToWorld(_center), rotation, radius, height, _staticActor ? Color::CornflowerBlue : Color::Orchid, 0, true);
else
@@ -57,10 +56,9 @@ void CapsuleCollider::OnDebugDrawSelected()
{
Quaternion rotation;
Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rotation);
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize);
const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize);
const Vector3 position = _transform.LocalToWorld(_center);
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, radius, height, Color::GreenYellow, 0, false);
@@ -92,9 +90,8 @@ void CapsuleCollider::UpdateBounds()
void CapsuleCollider::GetGeometry(CollisionShape& collision)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize);
const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize);
collision.SetCapsule(radius, height * 0.5f);
}

View File

@@ -21,6 +21,7 @@ CharacterController::CharacterController(const SpawnParams& params)
, _upDirection(Vector3::Up)
, _gravityDisplacement(Vector3::Zero)
, _nonWalkableMode(NonWalkableModes::PreventClimbing)
, _originMode(OriginModes::CapsuleCenter)
, _lastFlags(CollisionFlags::None)
{
_contactOffset = 10.0f;
@@ -35,9 +36,7 @@ void CharacterController::SetRadius(const float value)
{
if (value == _radius)
return;
_radius = value;
UpdateSize();
UpdateBounds();
}
@@ -51,9 +50,7 @@ void CharacterController::SetHeight(const float value)
{
if (value == _height)
return;
_height = value;
UpdateSize();
UpdateBounds();
}
@@ -87,6 +84,23 @@ void CharacterController::SetNonWalkableMode(NonWalkableModes value)
PhysicsBackend::SetControllerNonWalkableMode(_controller, (int32)value);
}
CharacterController::OriginModes CharacterController::GetOriginMode() const
{
return _originMode;
}
void CharacterController::SetOriginMode(OriginModes value)
{
if (_originMode == value)
return;
_originMode = value;
if (_controller)
{
DeleteController();
CreateController();
}
}
float CharacterController::GetStepOffset() const
{
return _stepOffset;
@@ -96,15 +110,11 @@ void CharacterController::SetStepOffset(float value)
{
if (value == _stepOffset)
return;
_stepOffset = value;
if (_controller)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float contactOffset = Math::Max(_contactOffset, ZeroTolerance);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, CC_MIN_SIZE);
float height, radius;
GetControllerSize(height, radius);
PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - CC_MIN_SIZE));
}
}
@@ -169,17 +179,69 @@ CharacterController::CollisionFlags CharacterController::SimpleMove(const Vector
CharacterController::CollisionFlags CharacterController::Move(const Vector3& displacement)
{
CollisionFlags result = CollisionFlags::None;
if (_controller)
if (_controller && !_isUpdatingTransform)
{
// Perform move
const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds();
result = (CollisionFlags)PhysicsBackend::MoveController(_controller, _shape, displacement, _minMoveDistance, deltaTime);
_lastFlags = result;
Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center;
// Update position
Vector3 position;
if (_originMode == OriginModes::Base)
position = PhysicsBackend::GetControllerBasePosition(_controller);
else
position = PhysicsBackend::GetControllerPosition(_controller);
position -= _center;
_isUpdatingTransform = true;
SetPosition(position);
_isUpdatingTransform = false;
}
return result;
}
void CharacterController::Resize(float height, float radius)
{
const float heightDiff = height - _height;
const float radiusDiff = radius - _radius;
if (Math::IsZero(heightDiff) && Math::IsZero(radiusDiff))
return;
_height = height;
_radius = radius;
if (_controller)
{
float centerDiff = heightDiff * 0.5f + radiusDiff;
// Change physics size
GetControllerSize(height, radius);
PhysicsBackend::SetControllerSize(_controller, radius, height);
Vector3 positionDelta = _upDirection * centerDiff;
// Change physics position to maintain feet placement (base)
Vector3 position;
switch (_originMode)
{
case OriginModes::CapsuleCenter:
position = PhysicsBackend::GetControllerPosition(_controller);
position += positionDelta;
_center += positionDelta;
PhysicsBackend::SetControllerPosition(_controller, position);
break;
case OriginModes::Base:
position = PhysicsBackend::GetControllerBasePosition(_controller);
position += positionDelta;
PhysicsBackend::SetControllerBasePosition(_controller, position);
break;
}
// Change actor position
_isUpdatingTransform = true;
SetPosition(position - _center);
_isUpdatingTransform = false;
}
UpdateBounds();
}
#if USE_EDITOR
#include "Engine/Debug/DebugDraw.h"
@@ -187,23 +249,38 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
void CharacterController::DrawPhysicsDebug(RenderView& view)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const Vector3 position = _transform.LocalToWorld(_center);
Quaternion rotation = Quaternion::Euler(90, 0, 0);
const Vector3 position = GetControllerPosition();
if (view.Mode == ViewMode::PhysicsColliders)
DEBUG_DRAW_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true);
DEBUG_DRAW_CAPSULE(position, rotation, _radius, _height, Color::LightYellow, 0, true);
else
DEBUG_DRAW_WIRE_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow * 0.8f, 0, true);
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow * 0.8f, 0, true);
}
void CharacterController::OnDebugDrawSelected()
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const Vector3 position = _transform.LocalToWorld(_center);
DEBUG_DRAW_WIRE_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false);
Quaternion rotation = Quaternion::Euler(90, 0, 0);
const Vector3 position = GetControllerPosition();
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow, 0, false);
if (_controller)
{
// Physics backend capsule shape
float height, radius;
GetControllerSize(height, radius);
Vector3 pos = PhysicsBackend::GetControllerPosition(_controller);
DEBUG_DRAW_WIRE_CAPSULE(pos, rotation, radius, height, Color::Blue.AlphaMultiplied(0.2f), 0, false);
#if 0
// More technical visuals debugging
Vector3 base = PhysicsBackend::GetControllerBasePosition(_controller);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(base, 5.0f), Color::Red, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos, 4.0f), Color::Red, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos + Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos - Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos + Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos - Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false);
DEBUG_DRAW_WIRE_CYLINDER(pos, Quaternion::Identity, radius, height, Color::Red.AlphaMultiplied(0.2f), 0, false);
#endif
}
// Base
Collider::OnDebugDrawSelected();
@@ -215,10 +292,14 @@ void CharacterController::CreateController()
{
// Create controller
ASSERT(_controller == nullptr && _shape == nullptr);
_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, Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape);
_cachedScale = GetScale().GetAbsolute().MaxValue();
float height, radius;
GetControllerSize(height, radius);
Vector3 position = _center;
if (_originMode == OriginModes::Base)
position += _upDirection * (_height * 0.5f + _radius);
position = _transform.LocalToWorld(position);
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, radius, height, _stepOffset, _shape);
// Setup
PhysicsBackend::SetControllerUpDirection(_controller, _upDirection);
@@ -241,13 +322,35 @@ void CharacterController::UpdateSize() const
{
if (_controller)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), CC_MIN_SIZE);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
float height, radius;
GetControllerSize(height, radius);
PhysicsBackend::SetControllerSize(_controller, radius, height);
}
}
Vector3 CharacterController::GetControllerPosition() const
{
Vector3 position = _center;
if (_originMode == OriginModes::Base)
position += _upDirection * (_height * 0.5f + _radius);
position = _transform.LocalToWorld(position);
return position;
}
void CharacterController::GetControllerSize(float& height, float& radius) const
{
// Use absolute values including scale
height = Math::Abs(_height) * _cachedScale;
radius = Math::Abs(_radius) * _cachedScale;
// Exclude contact offset around the capsule (otherwise character floats in the air)
radius = radius - Math::Max(_contactOffset, 0.0f);
// Prevent too small controllers
height = Math::Max(height, CC_MIN_SIZE);
radius = Math::Max(radius, CC_MIN_SIZE);
}
void CharacterController::CreateShape()
{
// Not used
@@ -255,10 +358,10 @@ void CharacterController::CreateShape()
void CharacterController::UpdateBounds()
{
const float scaling = GetScale().GetAbsolute().MaxValue();
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const Vector3 position = _transform.LocalToWorld(_center);
_cachedScale = GetScale().GetAbsolute().MaxValue();
float height, radius;
GetControllerSize(height, radius);
const Vector3 position = GetControllerPosition();
const Vector3 extent(radius, height * 0.5f + radius, radius);
_box = BoundingBox(position - extent, position + extent);
BoundingSphere::FromBox(_box, _sphere);
@@ -292,6 +395,21 @@ RigidBody* CharacterController::GetAttachedRigidBody() const
return nullptr;
}
void CharacterController::SetCenter(const Vector3& value)
{
if (value == _center)
return;
Vector3 delta = value - _center;
_center = value;
if (_controller)
{
// Change physics position while maintaining actor placement
Vector3 position = PhysicsBackend::GetControllerPosition(_controller);
position += _upDirection * delta;
PhysicsBackend::SetControllerPosition(_controller, position);
}
}
void CharacterController::OnActiveTransformChanged()
{
if (!_shape)
@@ -300,7 +418,12 @@ void CharacterController::OnActiveTransformChanged()
// Change actor transform (but with locking)
ASSERT(!_isUpdatingTransform);
_isUpdatingTransform = true;
const Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center;
Vector3 position;
if (_originMode == OriginModes::Base)
position = PhysicsBackend::GetControllerBasePosition(_controller);
else
position = PhysicsBackend::GetControllerPosition(_controller);
position -= _center;
SetPosition(position);
_isUpdatingTransform = false;
@@ -319,7 +442,7 @@ void CharacterController::UpdateGeometry()
return;
// Setup shape geometry
_cachedScale = GetScale();
_cachedScale = GetScale().GetAbsolute().MaxValue();
UpdateSize();
}
@@ -382,8 +505,11 @@ void CharacterController::OnTransformChanged()
const Vector3 position = _transform.LocalToWorld(_center);
if (!_isUpdatingTransform && _controller)
{
PhysicsBackend::SetControllerPosition(_controller, position);
const Float3 scale = GetScale();
if (_originMode == OriginModes::Base)
PhysicsBackend::SetControllerBasePosition(_controller, position);
else
PhysicsBackend::SetControllerPosition(_controller, position);
const float scale = GetScale().GetAbsolute().MaxValue();
if (_cachedScale != scale)
UpdateGeometry();
UpdateBounds();

View File

@@ -41,6 +41,22 @@ public:
Below = 1 << 2,
};
/// <summary>
/// Specifies how a character controller capsule placement.
/// </summary>
API_ENUM() enum class OriginModes
{
/// <summary>
/// Character origin starts at capsule center (including Center offset properly).
/// </summary>
CapsuleCenter,
/// <summary>
/// Character origin starts at capsule base position aka character feet placement.
/// </summary>
Base,
};
/// <summary>
/// Specifies how a character controller interacts with non-walkable parts.
/// </summary>
@@ -69,6 +85,7 @@ private:
Vector3 _upDirection;
Vector3 _gravityDisplacement;
NonWalkableModes _nonWalkableMode;
OriginModes _originMode;
CollisionFlags _lastFlags;
public:
@@ -84,13 +101,13 @@ public:
API_PROPERTY() void SetRadius(float value);
/// <summary>
/// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale.
/// Gets the height of the capsule as a distance between the two sphere centers at the end of the capsule. The capsule height is measured in the object's local space and will be scaled by the actor's world scale.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
float GetHeight() const;
/// <summary>
/// Sets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale.
/// Sets the height of the capsule as a distance between the two sphere centers at the end of the capsule. The capsule height is measured in the object's local space and will be scaled by the actor's world scale.
/// </summary>
API_PROPERTY() void SetHeight(float value);
@@ -116,6 +133,17 @@ public:
/// </summary>
API_PROPERTY() void SetNonWalkableMode(NonWalkableModes value);
/// <summary>
/// Gets the position origin placement mode.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(216), DefaultValue(OriginModes.CapsuleCenter), EditorDisplay(\"Character Controller\")")
OriginModes GetOriginMode() const;
/// <summary>
/// Sets the position origin placement mode.
/// </summary>
API_PROPERTY() void SetOriginMode(OriginModes value);
/// <summary>
/// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controllers height or it will generate an error.
/// </summary>
@@ -194,6 +222,13 @@ public:
/// <returns>The collision flags. It can be used to trigger various character animations.</returns>
API_FUNCTION() CollisionFlags Move(const Vector3& displacement);
/// <summary>
/// Updates the character height and center position to ensure its feet position stays the same. This can be used to implement a 'crouch' functionality for example. Maintains the same actor position to stay in the middle of capsule by adjusting center of collider accordingly to height difference.
/// </summary>
/// <param name="height">The height of the capsule, measured in the object's local space.</param>
/// <param name="radius">The radius of the capsule, measured in the object's local space.</param>
API_FUNCTION() void Resize(float height, float radius);
protected:
/// <summary>
/// Creates the physics actor.
@@ -210,6 +245,10 @@ protected:
/// </summary>
void UpdateSize() const;
private:
Vector3 GetControllerPosition() const;
void GetControllerSize(float& height, float& radius) const;
public:
// [Collider]
#if USE_EDITOR
@@ -220,6 +259,7 @@ public:
void AddMovement(const Vector3& translation, const Quaternion& rotation) override;
bool CanAttach(RigidBody* rigidBody) const override;
RigidBody* GetAttachedRigidBody() const override;
void SetCenter(const Vector3& value) override;
// [IPhysicsActor]
void OnActiveTransformChanged() override;

View File

@@ -205,7 +205,7 @@ void Collider::CreateShape()
ASSERT(_shape == nullptr);
// Setup shape geometry
_cachedScale = GetScale();
_cachedScale = GetScale().GetAbsolute().MaxValue();
CollisionShape shape;
GetGeometry(shape);
@@ -222,7 +222,7 @@ void Collider::UpdateGeometry()
return;
// Setup shape geometry
_cachedScale = GetScale();
_cachedScale = GetScale().GetAbsolute().MaxValue();
CollisionShape shape;
GetGeometry(shape);
@@ -427,7 +427,7 @@ void Collider::OnTransformChanged()
}
}
const Float3 scale = GetScale();
const float scale = GetScale().GetAbsolute().MaxValue();
if (_cachedScale != scale)
UpdateGeometry();
UpdateBounds();

View File

@@ -24,7 +24,7 @@ protected:
bool _isTrigger;
void* _shape;
void* _staticActor;
Float3 _cachedScale;
float _cachedScale;
float _contactOffset;
Vector3 _cachedLocalPosePos;
Quaternion _cachedLocalPoseRot;
@@ -61,7 +61,7 @@ public:
/// <summary>
/// Sets the center of the collider, measured in the object's local space.
/// </summary>
API_PROPERTY() void SetCenter(const Vector3& value);
API_PROPERTY() virtual void SetCenter(const Vector3& value);
/// <summary>
/// Gets the contact offset. Colliders whose distance is less than the sum of their ContactOffset values will generate contacts. The contact offset must be positive. Contact offset allows the collision detection system to predictively enforce the contact constraint even when the objects are slightly separated.

View File

@@ -131,7 +131,7 @@ void MeshCollider::UpdateBounds()
void MeshCollider::GetGeometry(CollisionShape& collision)
{
// Prepare scale
Float3 scale = _cachedScale;
Float3 scale = _transform.Scale;
const float minSize = 0.001f;
Float3 scaleAbs = scale.GetAbsolute();
if (scaleAbs.X < minSize)

View File

@@ -67,8 +67,7 @@ void SphereCollider::UpdateBounds()
void SphereCollider::GetGeometry(CollisionShape& collision)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Abs(_radius) * scaling;
const float radius = Math::Abs(_radius) * _cachedScale;
const float minSize = 0.001f;
collision.SetSphere(Math::Max(radius, minSize));
}

View File

@@ -259,8 +259,7 @@ void SplineCollider::GetGeometry(CollisionShape& collision)
}
// Prepare scale
Float3 scale = _cachedScale;
scale = Float3::Max(scale.GetAbsolute(), minSize);
Float3 scale = Float3::Max(_transform.Scale.GetAbsolute(), minSize);
// TODO: add support for cooking collision for static splines in editor and reusing it in game

View File

@@ -3155,10 +3155,9 @@ void* PhysicsBackend::CreateController(void* scene, IPhysicsActor* actor, Physic
desc.material = (PxMaterial*)((PhysicalMaterial*)material->Instance)->GetPhysicsMaterial();
else
desc.material = DefaultMaterial;
const float minSize = 0.001f;
desc.height = Math::Max(height, minSize);
desc.radius = Math::Max(radius - Math::Max(contactOffset, 0.0f), minSize);
desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - minSize);
desc.height = height;
desc.radius = radius;
desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - 0.001f);
auto controllerPhysX = (PxCapsuleController*)scenePhysX->ControllerManager->createController(desc);
PxRigidActor* actorPhysX = controllerPhysX->getActor();
ASSERT(actorPhysX && actorPhysX->getNbShapes() == 1);
@@ -3183,7 +3182,7 @@ void PhysicsBackend::SetControllerSize(void* controller, float radius, float hei
{
auto controllerPhysX = (PxCapsuleController*)controller;
controllerPhysX->setRadius(radius);
controllerPhysX->resize(height);
controllerPhysX->setHeight(height);
}
void PhysicsBackend::SetControllerSlopeLimit(void* controller, float value)
@@ -3204,6 +3203,20 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value)
controllerPhysX->setStepOffset(value);
}
Vector3 PhysicsBackend::GetControllerBasePosition(void* controller)
{
auto controllerPhysX = (PxCapsuleController*)controller;
const Vector3 origin = SceneOrigins[controllerPhysX->getScene()];
return P2C(controllerPhysX->getFootPosition()) + origin;
}
void PhysicsBackend::SetControllerBasePosition(void* controller, const Vector3& value)
{
auto controllerPhysX = (PxCapsuleController*)controller;
const Vector3 sceneOrigin = SceneOrigins[controllerPhysX->getScene()];
controllerPhysX->setFootPosition(PxExtendedVec3(value.X - sceneOrigin.X, value.Y - sceneOrigin.Y, value.Z - sceneOrigin.Z));
}
Vector3 PhysicsBackend::GetControllerUpDirection(void* controller)
{
auto controllerPhysX = (PxCapsuleController*)controller;

View File

@@ -248,6 +248,8 @@ public:
static void SetControllerSlopeLimit(void* controller, float value);
static void SetControllerNonWalkableMode(void* controller, int32 value);
static void SetControllerStepOffset(void* controller, float value);
static Vector3 GetControllerBasePosition(void* controller);
static void SetControllerBasePosition(void* controller, const Vector3& value);
static Vector3 GetControllerUpDirection(void* controller);
static void SetControllerUpDirection(void* controller, const Vector3& value);
static Vector3 GetControllerPosition(void* controller);

View File

@@ -43,17 +43,17 @@ void* WindowsPlatform::Instance = nullptr;
extern "C" {
static HANDLE dbgHelpLock;
void DbgHelpInit()
void FlaxDbgHelpInit()
{
dbgHelpLock = CreateMutexW(nullptr, FALSE, nullptr);
}
void DbgHelpLock()
void FlaxDbgHelpLock()
{
WaitForSingleObject(dbgHelpLock, INFINITE);
}
void DbgHelpUnlock()
void FlaxDbgHelpUnlock()
{
ReleaseMutex(dbgHelpLock);
}
@@ -552,7 +552,7 @@ void WindowsPlatform::PreInit(void* hInstance)
#if CRASH_LOG_ENABLE
TCHAR buffer[MAX_PATH] = { 0 };
DbgHelpLock();
FlaxDbgHelpLock();
if (::GetModuleFileNameW(::GetModuleHandleW(nullptr), buffer, MAX_PATH))
SymbolsPath.Add(StringUtils::GetDirectoryName(buffer));
if (::GetEnvironmentVariableW(TEXT("_NT_SYMBOL_PATH"), buffer, MAX_PATH))
@@ -561,7 +561,7 @@ void WindowsPlatform::PreInit(void* hInstance)
options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS;
SymSetOptions(options);
OnSymbolsPathModified();
DbgHelpUnlock();
FlaxDbgHelpUnlock();
#endif
GetWindowsVersion(WindowsName, VersionMajor, VersionMinor, VersionBuild);
@@ -781,7 +781,7 @@ void WindowsPlatform::BeforeExit()
void WindowsPlatform::Exit()
{
#if CRASH_LOG_ENABLE
DbgHelpLock();
FlaxDbgHelpLock();
#if !TRACY_ENABLE
if (SymInitialized)
{
@@ -790,7 +790,7 @@ void WindowsPlatform::Exit()
}
#endif
SymbolsPath.Resize(0);
DbgHelpUnlock();
FlaxDbgHelpUnlock();
#endif
#if !PLATFORM_SDL
@@ -1298,14 +1298,14 @@ void* WindowsPlatform::LoadLibrary(const Char* filename)
#if CRASH_LOG_ENABLE
// Refresh modules info during next stack trace collecting to have valid debug symbols information
DbgHelpLock();
FlaxDbgHelpLock();
if (folder.HasChars() && !SymbolsPath.Contains(folder))
{
SymbolsPath.Add(folder);
SymbolsPath.Last().Replace('/', '\\');
OnSymbolsPathModified();
}
DbgHelpUnlock();
FlaxDbgHelpUnlock();
#endif
return handle;
@@ -1316,7 +1316,7 @@ void* WindowsPlatform::LoadLibrary(const Char* filename)
Array<PlatformBase::StackFrame> WindowsPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context)
{
Array<StackFrame> result;
DbgHelpLock();
FlaxDbgHelpLock();
// Initialize
HANDLE process = GetCurrentProcess();
@@ -1448,7 +1448,7 @@ Array<PlatformBase::StackFrame> WindowsPlatform::GetStackFrames(int32 skipCount,
}
}
DbgHelpUnlock();
FlaxDbgHelpUnlock();
return result;
}

View File

@@ -13,6 +13,7 @@
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
#if USE_EDITOR
#include "Engine/Platform/MessageBox.h"
#include "Engine/Graphics/GPUDevice.h"
#endif
#include "Engine/Utilities/AnsiPathTempFile.h"
@@ -358,6 +359,9 @@ HRESULT LoadFromEXRFile(const StringView& path, DirectX::ScratchImage& image)
free(pixels);
return result;
#else
#if USE_EDITOR
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
#endif
LOG(Warning, "EXR format is not supported.");
return E_FAIL;
#endif

View File

@@ -13,6 +13,7 @@
#include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Utilities/AnsiPathTempFile.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/MessageBox.h"
#define STBI_ASSERT(x) ASSERT(x)
#define STBI_MALLOC(sz) Allocator::Allocate(sz)
@@ -286,21 +287,27 @@ bool TextureTool::ExportTextureStb(ImageType type, const StringView& path, const
break;
}
case ImageType::GIF:
MessageBox::Show(TEXT("GIF format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "GIF format is not supported.");
break;
case ImageType::TIFF:
MessageBox::Show(TEXT("TIFF format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "GIF format is not supported.");
break;
case ImageType::DDS:
MessageBox::Show(TEXT("DDS format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "DDS format is not supported.");
break;
case ImageType::RAW:
MessageBox::Show(TEXT("RAW format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "RAW format is not supported.");
break;
case ImageType::EXR:
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "EXR format is not supported.");
break;
default:
MessageBox::Show(TEXT("Unknown format."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "Unknown format.");
break;
}
@@ -434,17 +441,21 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu
free(pixels);
#else
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "EXR format is not supported.");
#endif
break;
}
case ImageType::DDS:
MessageBox::Show(TEXT("DDS format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "DDS format is not supported.");
break;
case ImageType::TIFF:
MessageBox::Show(TEXT("TIFF format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "TIFF format is not supported.");
break;
default:
MessageBox::Show(TEXT("Unknown format."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "Unknown format.");
return true;
}

View File

@@ -301,7 +301,7 @@ namespace FlaxEngine.GUI
else
{
text = _watermarkText;
if (text.Length > 0)
if (text?.Length > 0)
{
Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial);
}