Debugging JoltPhysics in UE5

Published by

on

I have been struggling with getting JoltPhysics to play nicely in my code. What seems to work just fine in the JoltPhysic’s samples, utterly fails when I try to play inside the Unreal Editor. This post is a quick update on how I’m going to start debugging Jolt from inside UE5, thanks to Jolt exposing a debug renderer interface.

Hiding everything about Jolt from UE5

I don’t know if it’s the way Jolt manages it’s own memory and overloads the new operator or what, but exposing anything about Jolt to UE5 causes me massive headaches. Whether it’s random crashes due to extremely bizarre bugs, or missing symbols because I need to tell UE5 to compile with enabling Jolt to export symbols, I decided to just not deal with it any of those issues.

To do that, I only expose Jolt to my PMO library and wrap everything else. UE5 does not call any Jolt related code and only really interfaces with PMO and the flecs library. So far this has worked out quite nicely. But I’m finding it really hard to understand why my character is falling through the world.

The Debug Renderer

If you look through the Jolt Samples project, you’ll notice each test has access to a DebugRenderer class. They also expose a simple DebugRendererSimple interface. Thankfully, the author of Jolt built this in a way that makes it easy to inherit from and use with in which ever game / rendering engine you have.

Implementation in PMO

As I mentioned, I need to hide everything about Jolt from UE5, so to do that I’m going to make a proxy class (or Adapter if you wanna be all fancy). Much like how I did with my Logger, i’m going to create an interface that UE5 can inherit from. The abstract methods defined here are the ones that Jolt expects from their DebugRenderer interface, except I am using custom debug data types instead of the Jolt data types as I can’t expose anything about Jolt to UE5.

class DebugRenderer
{
public:
	virtual ~DebugRenderer() = default;
	
	virtual void DrawLine(DebugVec3 &From, DebugVec3 &To, uint32_t InColor) = 0;

	virtual void DrawTriangle(DebugVec3 &Vec1, DebugVec3 &Vec2, DebugVec3 &Vec3, uint32_t InColor, bool CastShadow) = 0;
};

DebugVec3 is a simple struct:

struct DebugVec3
{
	DebugVec3(float InX, float InY, float InZ) : X(InX), Y(InY), Z(InZ) {};
	float X = 0;
	float Y = 0;
	float Z = 0;
};

Quite basic, but now we can build the actual implemention for UE5 using their Debug draw mechanisms.

Implementation in UE5

The only real annoyance with implementing this in UE5 was that it requires the UWorld object. Which means I need to pass a World instance from the game client into my PMO module. Luckily almost every UObject passed around has a ->GetWorld() method.

Here’s the basic implementation of my debug::DebugRenderer interface in UE5:

class PMO_API UPMODebugRenderer : public debug::DebugRenderer
{
public:
    UPMODebugRenderer() {};

    void SetWorld(UWorld* InWorld) { World = InWorld; }

    virtual void DrawLine(debug::DebugVec3& From, debug::DebugVec3& To, uint32_t InColor) override
    {
        if (!World)
        {
            UE_LOG(LogTemp, Error, TEXT("Not Set!"));
            return;
        }
        FVector DebugFrom{ From.X, From.Z, From.Y };
        FVector DebugTo{ To.X, To.Z, To.Y };
        UKismetSystemLibrary::DrawDebugLine(World, DebugFrom, DebugTo, FLinearColor::Red, 120.f, 1.f);
    };

    virtual void DrawTriangle(debug::DebugVec3& Vec1, debug::DebugVec3& Vec2, debug::DebugVec3& Vec3, uint32_t InColor, bool CastShadow) override
    {
        DrawLine(Vec1, Vec2, InColor);
        DrawLine(Vec2, Vec3, InColor);
        DrawLine(Vec3, Vec1, InColor);
    };

    UWorld* World = nullptr;
};

Also when building the PMO module I needed to add the following define in my PMOLibrary.Build.cs:

PublicDefinitions.Add("JPH_DEBUG_RENDERER");

Otherwise building my PMOLibrary from UE5 failed!

Back to PMO for Jolt

With that wired together, there’s one last piece we need, passing the jolt engine a class that actually implements their DebugRendererSimple interface:

class JoltDebugRenderer : public JPH::DebugRendererSimple
    {

    public:
        JoltDebugRenderer(debug::DebugRenderer *InRenderer) : Renderer(InRenderer)
        {
            this->Initialize();
        }

        virtual void DrawLine(JPH::RVec3Arg InFrom, JPH::RVec3Arg InTo, JPH::ColorArg InColor) override
        {
            debug::DebugVec3 From{InFrom.GetX(), InFrom.GetY(), InFrom.GetZ()};
            debug::DebugVec3 To{InTo.GetX(), InTo.GetY(), InTo.GetZ()};
            Renderer->DrawLine(From, To, InColor.GetUInt32());
        }

        virtual void DrawTriangle(JPH::RVec3Arg InVec1, JPH::RVec3Arg InVec2, JPH::RVec3Arg InVec3, JPH::ColorArg InColor, ECastShadow inCastShadow) override
        {
            debug::DebugVec3 Vec1{InVec1.GetX(), InVec1.GetY(), InVec1.GetZ()};
            debug::DebugVec3 Vec2{InVec2.GetX(), InVec2.GetY(), InVec2.GetZ()};
            debug::DebugVec3 Vec3{InVec3.GetX(), InVec3.GetY(), InVec3.GetZ()};
            bool CastShadow = inCastShadow == ECastShadow::On ? true : false;

            Renderer->DrawTriangle(Vec1, Vec2, Vec3, InColor.GetUInt32(), CastShadow);
        }

        virtual void DrawText3D(JPH::RVec3Arg,const std::string_view &,JPH::ColorArg,float) override { }

        private:
            debug::DebugRenderer *Renderer; // ref to our proxy interface
    };

So now we just construct a JoltDebugRenderer object, and pass it in our UE5 debug renderer:

// Physics::InitializeWorld(..., config::Config &Config, ...)
if (Config.GetDebugRenderer())
{
	// Create our Physics systems' debug renderer
    DebugRenderer = std::make_unique<debug::JoltDebugRenderer>(Config.GetDebugRenderer());
}

Now inside our flecs ECS UpdateSimulation system, we can call the debug renderer:

// only print every 250th frame.
if (DebugRenderer && (FrameCount % 250 == 0))
{
	Log->Warn("Drawing Bodies");
	JPH::BodyManager::DrawSettings BodyDrawSettings;
	BodyDrawSettings.mDrawShapeWireframe = true;
	System->DrawBodies(BodyDrawSettings, DebugRenderer.get(), nullptr);
}

How it looks inside UE5

Screenshot 2024-05-11 211532.png
As you can see, those redlines are showing us exactly what Jolt sees for it’s known bodies. Also, it looks like it loaded my landscape correctly, so at least I know that works properly!

Pasted image 20240512190352.png
Annnndd… I’m falling through the world :(.

So besides my bug still existing, another major problem is doing this debug rendering causes everything to be EXTREMELY SLOW. Like .01 FPS slow. It’s unbearable, but at least I can see that it’s loading the mesh properly.

Where to from here?

While this is great that I can now see my model is being loaded, I still can’t for the life of me figure out why my character is falling through the world. I suspected it was a problem with how I’m serializing/deserializing but it works fine in the Jolt samples. My next major area of investigation is looking at the way flecs and Jolt manages memory. I wonder if how flecs works is causing some objects reference counts to go to 0 and be deleted from out from under me and Jolt?

I’ve implemented all the move/delete/copy constructors on my ECS components to get a better understanding. I have noticed flecs moves and deletes objects quite often, meaning be super careful what you put in a components destructors! I was calling Physics->RemoveFromWorld(object) and was seeing the destructor called many times on my character class. This is apparently due to how flecs moves objects around in various tables for it’s storage, but came as a surprise to me.