Fix debug drawing wheeled vehicle in prefab viewport

#3591
This commit is contained in:
Wojtek Figat
2025-08-29 21:03:44 +02:00
parent 5222f1d35c
commit 9fafb47abb
22 changed files with 102 additions and 100 deletions

View File

@@ -663,10 +663,7 @@ namespace FlaxEditor.Viewport
if ((view.Flags & ViewFlags.PhysicsDebug) != 0 || view.Mode == ViewMode.PhysicsColliders)
{
foreach (var actor in _debugDrawActors)
{
if (actor is Collider c && c.IsActiveInHierarchy)
DebugDraw.DrawColliderDebugPhysics(c, renderContext.View);
}
DebugDraw.DrawDebugPhysics(actor, renderContext.View);
}
// Draw lights debug

View File

@@ -30,6 +30,7 @@
#include "Editor/Editor.h"
#include "Engine/Level/Actors/Light.h"
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Physics/Actors/IPhysicsDebug.h"
#endif
// Debug draw service configuration
@@ -1027,11 +1028,12 @@ void DebugDraw::DrawActorsTree(Actor* actor)
#if USE_EDITOR
void DebugDraw::DrawColliderDebugPhysics(Collider* collider, RenderView& view)
void DebugDraw::DrawDebugPhysics(Actor* actor, RenderView& view)
{
if (!collider)
if (!actor || !actor->IsActiveInHierarchy())
return;
collider->DrawPhysicsDebug(view);
if (auto* physicsDebug = dynamic_cast<IPhysicsDebug*>(actor))
physicsDebug->DrawPhysicsDebug(view);
}
void DebugDraw::DrawLightDebug(Light* light, RenderView& view)

View File

@@ -102,11 +102,11 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
#if USE_EDITOR
/// <summary>
/// Draws the physics debug shapes for the given collider. Editor Only
/// Draws the physics debug shapes for the given actor. Editor Only
/// </summary>
/// <param name="collider">The collider to draw.</param>
/// <param name="actor">The actor to draw.</param>
/// <param name="view">The render view to draw in.</param>
API_FUNCTION() static void DrawColliderDebugPhysics(Collider* collider, RenderView& view);
API_FUNCTION() static void DrawDebugPhysics(Actor* actor, RenderView& view);
/// <summary>
/// Draws the light debug shapes for the given light. Editor Only

View File

@@ -9,6 +9,7 @@
#include "Engine/Threading/JobSystem.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Physics/Actors/IPhysicsDebug.h"
ISceneRenderingListener::~ISceneRenderingListener()
{
@@ -91,10 +92,10 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
if (EnumHasAnyFlags(view.Flags, ViewFlags::PhysicsDebug) || view.Mode == ViewMode::PhysicsColliders)
{
PROFILE_CPU_NAMED("PhysicsDebug");
const PhysicsDebugCallback* physicsDebugData = PhysicsDebug.Get();
const auto* physicsDebugData = PhysicsDebug.Get();
for (int32 i = 0; i < PhysicsDebug.Count(); i++)
{
physicsDebugData[i](view);
physicsDebugData[i]->DrawPhysicsDebug(view);
}
}

View File

@@ -11,6 +11,7 @@
class SceneRenderTask;
class SceneRendering;
class IPhysicsDebug;
struct PostProcessSettings;
struct RenderContext;
struct RenderContextBatch;
@@ -74,7 +75,6 @@ public:
class FLAXENGINE_API SceneRendering
{
#if USE_EDITOR
typedef Function<void(RenderView&)> PhysicsDebugCallback;
typedef Function<void(RenderView&)> LightsDebugCallback;
friend class ViewportIconsRendererService;
#endif
@@ -105,7 +105,7 @@ public:
private:
#if USE_EDITOR
Array<PhysicsDebugCallback> PhysicsDebug;
Array<IPhysicsDebug*> PhysicsDebug;
Array<LightsDebugCallback> LightsDebug;
Array<Actor*> ViewportIcons;
#endif
@@ -149,20 +149,14 @@ public:
}
#if USE_EDITOR
template<class T, void(T::*Method)(RenderView&)>
FORCE_INLINE void AddPhysicsDebug(T* obj)
FORCE_INLINE void AddPhysicsDebug(IPhysicsDebug* obj)
{
PhysicsDebugCallback f;
f.Bind<T, Method>(obj);
PhysicsDebug.Add(f);
PhysicsDebug.Add(obj);
}
template<class T, void(T::*Method)(RenderView&)>
void RemovePhysicsDebug(T* obj)
FORCE_INLINE void RemovePhysicsDebug(IPhysicsDebug* obj)
{
PhysicsDebugCallback f;
f.Bind<T, Method>(obj);
PhysicsDebug.Remove(f);
PhysicsDebug.Remove(obj);
}
template<class T, void(T::*Method)(RenderView&)>

View File

@@ -457,7 +457,7 @@ void Cloth::OnEnable()
{
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
#if USE_EDITOR
GetSceneRendering()->AddPhysicsDebug<Cloth, &Cloth::DrawPhysicsDebug>(this);
GetSceneRendering()->AddPhysicsDebug(this);
#endif
#if WITH_CLOTH
if (_cloth)
@@ -476,7 +476,7 @@ void Cloth::OnDisable()
PhysicsBackend::RemoveCloth(GetPhysicsScene()->GetPhysicsScene(), _cloth);
#endif
#if USE_EDITOR
GetSceneRendering()->RemovePhysicsDebug<Cloth, &Cloth::DrawPhysicsDebug>(this);
GetSceneRendering()->RemovePhysicsDebug(this);
#endif
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
}

View File

@@ -4,6 +4,7 @@
#include "Engine/Level/Actor.h"
#include "Engine/Level/Actors/ModelInstanceActor.h"
#include "IPhysicsDebug.h"
// Used internally to validate cloth data against invalid nan/inf values
#define USE_CLOTH_SANITY_CHECKS 0
@@ -12,6 +13,9 @@
/// Physical simulation actor for cloth objects made of vertices that are simulated as cloth particles with physical properties, forces, and constraints to affect cloth behavior.
/// </summary>
API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Cloth\"), ActorToolbox(\"Physics\")") class FLAXENGINE_API Cloth : public Actor
#if USE_EDITOR
, public IPhysicsDebug
#endif
{
DECLARE_SCENE_OBJECT(Cloth);
@@ -364,9 +368,7 @@ protected:
void OnPhysicsSceneChanged(PhysicsScene* previous) override;
private:
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view);
#endif
ImplementPhysicsDebug;
bool CreateCloth();
void DestroyCloth();
void CalculateInvMasses(Array<float>& invMasses);

View File

@@ -0,0 +1,18 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#if USE_EDITOR
class FLAXENGINE_API IPhysicsDebug
{
public:
virtual void DrawPhysicsDebug(struct RenderView& view)
{
}
};
#define ImplementPhysicsDebug void DrawPhysicsDebug(RenderView& view)
#else
#define ImplementPhysicsDebug
#endif

View File

@@ -356,19 +356,19 @@ void WheeledVehicle::Setup()
void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
{
// Wheels shapes
for (const auto& data : _wheelsData)
for (const auto& wheel : _wheels)
{
int32 wheelIndex = 0;
for (; wheelIndex < _wheels.Count(); wheelIndex++)
{
if (_wheels[wheelIndex].Collider == data.Collider)
break;
}
if (wheelIndex == _wheels.Count())
break;
const auto& wheel = _wheels[wheelIndex];
if (wheel.Collider && wheel.Collider->GetParent() == this && !wheel.Collider->GetIsTrigger())
{
WheelData data = { wheel.Collider, wheel.Collider->GetLocalOrientation() };
for (auto& e : _wheelsData)
{
if (e.Collider == data.Collider)
{
data = e;
break;
}
}
const Vector3 currentPos = wheel.Collider->GetPosition();
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
const Quaternion wheelDebugOrientation = GetOrientation() * Quaternion::Euler(-data.State.RotationAngle, data.State.SteerAngle, 0) * Quaternion::Euler(90, 0, 90);
@@ -387,25 +387,28 @@ void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
void WheeledVehicle::OnDebugDrawSelected()
{
// Wheels shapes
for (const auto& data : _wheelsData)
for (const auto& wheel : _wheels)
{
int32 wheelIndex = 0;
for (; wheelIndex < _wheels.Count(); wheelIndex++)
{
if (_wheels[wheelIndex].Collider == data.Collider)
break;
}
if (wheelIndex == _wheels.Count())
break;
const auto& wheel = _wheels[wheelIndex];
if (wheel.Collider && wheel.Collider->GetParent() == this && !wheel.Collider->GetIsTrigger())
{
WheelData data = { wheel.Collider, wheel.Collider->GetLocalOrientation() };
for (auto& e : _wheelsData)
{
if (e.Collider == data.Collider)
{
data = e;
break;
}
}
const Vector3 currentPos = wheel.Collider->GetPosition();
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
const Quaternion wheelDebugOrientation = GetOrientation() * Quaternion::Euler(-data.State.RotationAngle, data.State.SteerAngle, 0) * Quaternion::Euler(90, 0, 90);
Transform actorPose = Transform::Identity, shapePose = Transform::Identity;
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
Transform actorPose = GetTransform(), shapePose = wheel.Collider->GetLocalTransform();
actorPose.Scale = Float3::One;
if (_actor)
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
if (wheel.Collider->GetPhysicsShape())
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(actorPose.LocalToWorld(shapePose.Translation), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
@@ -561,14 +564,14 @@ void WheeledVehicle::BeginPlay(SceneBeginData* data)
#endif
#if USE_EDITOR
GetSceneRendering()->AddPhysicsDebug<WheeledVehicle, &WheeledVehicle::DrawPhysicsDebug>(this);
GetSceneRendering()->AddPhysicsDebug(this);
#endif
}
void WheeledVehicle::EndPlay()
{
#if USE_EDITOR
GetSceneRendering()->RemovePhysicsDebug<WheeledVehicle, &WheeledVehicle::DrawPhysicsDebug>(this);
GetSceneRendering()->RemovePhysicsDebug(this);
#endif
#if WITH_VEHICLE

View File

@@ -5,12 +5,16 @@
#include "Engine/Physics/Actors/RigidBody.h"
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#include "IPhysicsDebug.h"
/// <summary>
/// Representation of the car vehicle that uses wheels. Built on top of the RigidBody with collider representing its chassis shape and wheels.
/// </summary>
/// <seealso cref="RigidBody" />
API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorToolbox(\"Physics\")") class FLAXENGINE_API WheeledVehicle : public RigidBody
#if USE_EDITOR
, public IPhysicsDebug
#endif
{
friend class PhysicsBackend;
friend struct ScenePhysX;
@@ -644,13 +648,9 @@ public:
/// </summary>
API_FUNCTION() void Setup();
private:
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view);
#endif
public:
// [Vehicle]
ImplementPhysicsDebug;
#if USE_EDITOR
void OnDebugDrawSelected() override;
#endif

View File

@@ -58,9 +58,7 @@ public:
protected:
// [Collider]
ImplementPhysicsDebug;
void UpdateBounds() override;
void GetGeometry(CollisionShape& collision) override;
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view) override;
#endif
};

View File

@@ -62,9 +62,7 @@ public:
protected:
// [Collider]
ImplementPhysicsDebug;
void UpdateBounds() override;
void GetGeometry(CollisionShape& collision) override;
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view) override;
#endif
};

View File

@@ -267,13 +267,11 @@ public:
protected:
// [PhysicsActor]
ImplementPhysicsDebug;
void UpdateGeometry() override;
void GetGeometry(CollisionShape& collision) override;
void BeginPlay(SceneBeginData* data) override;
void EndPlay() override;
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view) override;
#endif
void OnActiveInTreeChanged() override;
void OnEnable() override;
void OnDisable() override;

View File

@@ -147,7 +147,7 @@ void Collider::OnEnable()
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger)
GetScene()->Navigation.Actors.Add(this);
#if USE_EDITOR
GetSceneRendering()->AddPhysicsDebug<Collider, &Collider::DrawPhysicsDebug>(this);
GetSceneRendering()->AddPhysicsDebug(this);
#endif
PhysicsColliderActor::OnEnable();
@@ -160,7 +160,7 @@ void Collider::OnDisable()
if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger)
GetScene()->Navigation.Actors.Remove(this);
#if USE_EDITOR
GetSceneRendering()->RemovePhysicsDebug<Collider, &Collider::DrawPhysicsDebug>(this);
GetSceneRendering()->RemovePhysicsDebug(this);
#endif
}
@@ -286,14 +286,6 @@ void Collider::RemoveStaticActor()
_staticActor = nullptr;
}
#if USE_EDITOR
void Collider::DrawPhysicsDebug(RenderView& view)
{
}
#endif
void Collider::OnMaterialChanged()
{
// Update the shape material

View File

@@ -6,6 +6,7 @@
#include "Engine/Content/JsonAsset.h"
#include "Engine/Content/JsonAssetReference.h"
#include "Engine/Physics/Actors/PhysicsColliderActor.h"
#include "Engine/Physics/Actors/IPhysicsDebug.h"
struct RayCastHit;
class RigidBody;
@@ -16,6 +17,9 @@ class RigidBody;
/// <seealso cref="Actor" />
/// <seealso cref="PhysicsColliderActor" />
API_CLASS(Abstract) class FLAXENGINE_API Collider : public PhysicsColliderActor
#if USE_EDITOR
, public IPhysicsDebug
#endif
{
API_AUTO_SERIALIZATION();
DECLARE_SCENE_OBJECT_ABSTRACT(Collider);
@@ -165,9 +169,6 @@ public:
void ClosestPoint(const Vector3& point, Vector3& result) const final;
bool ContainsPoint(const Vector3& point) const final;
#if USE_EDITOR
virtual void DrawPhysicsDebug(RenderView& view);
#endif
protected:
// [PhysicsColliderActor]

View File

@@ -37,9 +37,7 @@ public:
protected:
// [Collider]
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view) override;
#endif
ImplementPhysicsDebug;
void UpdateBounds() override;
void GetGeometry(CollisionShape& collision) override;
};

View File

@@ -42,9 +42,7 @@ public:
protected:
// [Collider]
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view) override;
#endif
ImplementPhysicsDebug;
void UpdateBounds() override;
void GetGeometry(CollisionShape& collision) override;
};

View File

@@ -67,9 +67,7 @@ public:
protected:
// [Collider]
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view) override;
#endif
ImplementPhysicsDebug;
void UpdateBounds() override;
void GetGeometry(CollisionShape& collision) override;
};

View File

@@ -298,7 +298,7 @@ void Joint::EndPlay()
void Joint::OnEnable()
{
GetSceneRendering()->AddPhysicsDebug<Joint, &Joint::DrawPhysicsDebug>(this);
GetSceneRendering()->AddPhysicsDebug(this);
// Base
Actor::OnEnable();
@@ -306,7 +306,7 @@ void Joint::OnEnable()
void Joint::OnDisable()
{
GetSceneRendering()->RemovePhysicsDebug<Joint, &Joint::DrawPhysicsDebug>(this);
GetSceneRendering()->RemovePhysicsDebug(this);
// Base
Actor::OnDisable();

View File

@@ -5,6 +5,7 @@
#include "Engine/Level/Actor.h"
#include "Engine/Physics/Types.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#include "Engine/Physics/Actors/IPhysicsDebug.h"
class IPhysicsActor;
@@ -17,6 +18,9 @@ class IPhysicsActor;
/// </remarks>
/// <seealso cref="Actor" />
API_CLASS(Abstract) class FLAXENGINE_API Joint : public Actor
#if USE_EDITOR
, public IPhysicsDebug
#endif
{
DECLARE_SCENE_OBJECT_ABSTRACT(Joint);
protected:
@@ -174,9 +178,7 @@ protected:
Vector3 GetTargetPosition() const;
Quaternion GetTargetOrientation() const;
virtual void* CreateJoint(const struct PhysicsJointDesc& desc) = 0;
#if USE_EDITOR
virtual void DrawPhysicsDebug(RenderView& view);
#endif
ImplementPhysicsDebug;
private:
void Delete();

View File

@@ -845,7 +845,7 @@ void Terrain::OnEnable()
GetScene()->Navigation.Actors.Add(this);
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
#if TERRAIN_USE_PHYSICS_DEBUG
GetSceneRendering()->AddPhysicsDebug<Terrain, &Terrain::DrawPhysicsDebug>(this);
GetSceneRendering()->AddPhysicsDebug(this);
#endif
void* scene = GetPhysicsScene()->GetPhysicsScene();
for (int32 i = 0; i < _patches.Count(); i++)
@@ -866,7 +866,7 @@ void Terrain::OnDisable()
GetScene()->Navigation.Actors.Remove(this);
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
#if TERRAIN_USE_PHYSICS_DEBUG
GetSceneRendering()->RemovePhysicsDebug<Terrain, &Terrain::DrawPhysicsDebug>(this);
GetSceneRendering()->RemovePhysicsDebug(this);
#endif
void* scene = GetPhysicsScene()->GetPhysicsScene();
for (int32 i = 0; i < _patches.Count(); i++)

View File

@@ -5,6 +5,7 @@
#include "Engine/Content/JsonAssetReference.h"
#include "Engine/Content/Assets/MaterialBase.h"
#include "Engine/Physics/Actors/PhysicsColliderActor.h"
#include "Engine/Physics/Actors/IPhysicsDebug.h"
class Terrain;
class TerrainChunk;
@@ -38,6 +39,9 @@ struct RenderView;
/// <seealso cref="Actor" />
/// <seealso cref="PhysicsColliderActor" />
API_CLASS(Sealed) class FLAXENGINE_API Terrain : public PhysicsColliderActor
#if USE_EDITOR
, public IPhysicsDebug
#endif
{
DECLARE_SCENE_OBJECT(Terrain);
friend Terrain;
@@ -441,9 +445,7 @@ 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:
#if TERRAIN_USE_PHYSICS_DEBUG
void DrawPhysicsDebug(RenderView& view);
#endif
ImplementPhysicsDebug;
bool DrawSetup(RenderContext& renderContext);
void DrawImpl(RenderContext& renderContext, HashSet<TerrainChunk*, class RendererAllocation>& drawnChunks);