Debug Render System

To incorporate debug rendering in Prodigy Engine, I designed a Debug Render System capable of handling its own cameras as well as being able to draw both 2D and 3D shapes in the scene.

Here’s what the scene looks like when drawing all debug shapes:

Debug Render objects supported by Prodigy Engine

To incorporate this, I implemented a DebugRenderer class which serves as the interface used by a game to call into. Below is the header for my DebugRender system:

//------------------------------------------------------------------------------------------------------------------------------
// System to render debugging data for specified objects in the system.
//------------------------------------------------------------------------------------------------------------------------------
enum eDebugRenderSpace 
{
	DEBUG_RENDER_SCREEN,          // renders in screen space (ie, backbuffer), defined when initializing the system 
	DEBUG_RENDER_WORLD,           // is rendered in the world; 
}; 

enum eDebugRenderMode
{
	DEBUG_RENDER_USE_DEPTH,    
	DEBUG_RENDER_ALWAYS,       // always draw, does not write to depth
	DEBUG_RENDER_XRAY,         // render behind (greater, no write), then in front (lequal, write)
							   // darken or thin the line during behind render to differentiate from it being in front; 
}; 

//------------------------------------------------------------------------------------------------------------------------------
struct DebugRenderOptionsT
{
	eDebugRenderSpace space       = DEBUG_RENDER_SCREEN; 
	eDebugRenderMode mode         = DEBUG_RENDER_USE_DEPTH; 

	Rgba beginColor               = Rgba::WHITE; 
	Rgba endColor                 = Rgba::RED; 

	// 2D common - these are where I will treat the "origin" 
	// of the screen for drawing - defaulting to bottom-left corner; 
	// used for SCREEN and CAMERA modes, ignored for WORLD
	Vec4 positionRatioAndOffset   = Vec4( 0.0f, 0.0f, 0.0f, 0.0f ); // this is bottom-left corner 

	// 3D (WORLD space) common (ignored for SCREEN & CAMERA)
	Matrix44 modelTransform       = Matrix44::IDENTITY; // local transform to do to the object; 
	
	bool relativeCoordinates = false;

	ObjectProperties* objectProperties = nullptr; 
};

//------------------------------------------------------------------------------------------------------------------------------
class DebugRender
{
public:
	DebugRender();
	~DebugRender();

	//Events for Event system and dev console
	static bool				DisableDebugRender( EventArgs& args );
	static bool				EnableDebugRender( EventArgs& args );
	static bool				ClearAllLiveObjects( EventArgs& args );
	static bool				ClearAllLiveScreenObjects( EventArgs& args );
	static bool				ClearAllLiveWorldObjects( EventArgs& args );

	void					Startup(RenderContext* renderContext);
	void					SetClientDimensions(int height, int width);
	void					SetDebugFont( BitmapFont* font);
	void					Shutdown();

	void					BeginFrame();
	void					EndFrame();
	void					Update(float deltaTime);
	void					CleanUpObjects();

	void					DebugRenderToScreen() const;		//This renders to the screen camera
	void					DebugRenderToCamera() const;		//This renders in the world camera

	void					Setup2DCamera() const;
	void					Setup3DCamera(Camera* const camera);
	void					SetObjectMatrixForPosition( Vec3 position ) const;
	void					SetObjectMatrixForBillBoard( Vec3 position ) const;
	
	void					SetWorldSize2D(const Vec2& worldMin, const Vec2& worldMax);
	Vec2					GetRelativePosInWorld2D(const Vec2& positionInWorld);

	Camera&					Get2DCamera();

	//------------------------------------------------------------------------------------------------------------------------------
	// 2D Debug Rendering (defaults to SCREEN)
	//------------------------------------------------------------------------------------------------------------------------------
	void				DebugRenderPoint2D( DebugRenderOptionsT options, const Vec2& position, float duration = 0.f, float size = DEFAULT_POINT_SIZE );
	void				DebugRenderLine2D( DebugRenderOptionsT options, const Vec2& start, const Vec2& end, float duration = 0.f, float lineWidth = DEFAULT_LINE_WIDTH );
	void				DebugRenderQuad2D( DebugRenderOptionsT options, AABB2 const &quad, float duration = 0.f, TextureView *view = nullptr); 
	void				DebugRenderWireQuad2D( DebugRenderOptionsT options, AABB2 const &quad, float duration = 0.f, float thickness = DEFAULT_WIRE_WIDTH_2D ); 
	void				DebugRenderDisc2D( DebugRenderOptionsT options, Disc2D const &disc, float duration = 0.f); 
	void				DebugRenderRing2D( DebugRenderOptionsT options, Disc2D const &disc, float duration = 0.f, float thickness = DEFAULT_DISC_THICKNESS ); 
	void				DebugRenderText2D( DebugRenderOptionsT options, const Vec2& startPosition, const Vec2& endPosition, char const *format, float fontHeight = DEFAULT_TEXT_HEIGHT, float duration = 0.f, ... );
	void				DebugRenderArrow2D( DebugRenderOptionsT options, const Vec2& start, const Vec2& end, float duration = 0.f, float lineWidth = DEFAULT_LINE_WIDTH ); 

	// Helpers
	void				DebugRenderPoint2D( const Vec2& position, float duration = 0.f, float size = DEFAULT_POINT_SIZE );
	void				DebugRenderLine2D( const Vec2& start, const Vec2& end, float duration = 0.f, float lineWidth = DEFAULT_LINE_WIDTH );
	void				DebugRenderQuad2D( AABB2 const &quad, float duration = 0.f, TextureView *view = nullptr); 
	void				DebugRenderWireQuad2D( AABB2 const &quad, float duration = 0.f, float thickness = DEFAULT_WIRE_WIDTH_2D ); 
	void				DebugRenderDisc2D( Disc2D const &disc, float duration = 0.f); 
	void				DebugRenderRing2D( Disc2D const &disc, float duration = 0.f, float thickness = DEFAULT_DISC_THICKNESS ); 
	void				DebugRenderText2D( const Vec2& startPosition, const Vec2& endPosition, char const *format, float fontHeight = DEFAULT_TEXT_HEIGHT, float duration = 0.f, ... );
	void				DebugRenderArrow2D( const Vec2& start, const Vec2& end, float duration = 0.f, float lineWidth = DEFAULT_LINE_WIDTH ); 


	//------------------------------------------------------------------------------------------------------------------------------
	// Text Logs
	//------------------------------------------------------------------------------------------------------------------------------
	void				DebugAddToLog( DebugRenderOptionsT options, char const* format, const Rgba& color = Rgba::ORANGE, float duration = 0.f, ...);
	void				DebugAddToLog( char const* format, const Rgba& color = Rgba::ORANGE, float duration = 0.f, ...);

	//------------------------------------------------------------------------------------------------------------------------------
	// 3D Rendering (will always default to WORLD)
	//------------------------------------------------------------------------------------------------------------------------------
	void				DebugRenderPoint( DebugRenderOptionsT options, const Vec3& position, float duration = 0.f, float size = DEFAULT_POINT_SIZE_3D, TextureView* texture = nullptr );
	void				DebugRenderLine( DebugRenderOptionsT options, const Vec3& start, const Vec3& end, float duration = 0.f, float lineWidth = DEFAULT_LINE_WIDTH_3D );
	void				DebugRenderSphere( DebugRenderOptionsT options, Vec3 center, float radius, float duration = 0.f, TextureView* texture = nullptr ); 
	void				DebugRenderBox( DebugRenderOptionsT options, const AABB3& box, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr ); 
	void				DebugRenderQuad( DebugRenderOptionsT options, const AABB2& quad, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr, bool billBoarded = true);
	void				DebugRenderCapsule(DebugRenderOptionsT options, const Capsule3D& capsule, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr);

	// Helpers
	void				DebugRenderPoint( const Vec3& position, float duration = 0.f, float size = DEFAULT_POINT_SIZE_3D, TextureView* texture = nullptr );
	void				DebugRenderLine(const Vec3& start, const Vec3& end, float duration = 0.f);
	void				DebugRenderSphere( Vec3 center, float radius, float duration = 0.f, TextureView* texture = nullptr ); 
	void				DebugRenderBox( const AABB3& box, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr ); 
	void				DebugRenderQuad( const AABB2& quad, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr, bool billBoarded = true);
	void				DebugRenderCapsule( const Capsule3D& capsule, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr);

	//Render Wire shapes
	void				DebugRenderWireSphere( DebugRenderOptionsT options, Vec3 center, float radius, float duration = 0.f, TextureView* texture = nullptr ); 
	void				DebugRenderWireBox( DebugRenderOptionsT options, const AABB3& box, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr ); 
	void				DebugRenderWireCapsule(DebugRenderOptionsT options, const Capsule3D& capsule, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr);
	void				DebugRenderWireCapsule(const Capsule3D& capsule, const Vec3& position, float duration = 0.f, TextureView* texture = nullptr);

	void				DebugRenderArrow( DebugRenderOptionsT options, Vec3 start, Vec3 end, float base_thickness, float head_thickness ); 
	void				DebugRenderBasis( DebugRenderOptionsT options, Matrix44 const &basis, float lineWidth = DEFAULT_LINE_WIDTH ); 

	//------------------------------------------------------------------------------------------------------------------------------
	// Rendering 3D Text
	//------------------------------------------------------------------------------------------------------------------------------
	void				DebugRenderText3D( DebugRenderOptionsT options, const Vec3& position, const Vec2& pivot, char const *format, float fontHeight = DEFAULT_TEXT_HEIGHT_3D, float duration = 0.f, bool isBillboarded = true, ... );

private:

	void							DebugRenderToLog() const;				//This renders all the debug logs to the screen
	
	//Draw methods 2D
	void							DrawPoint2D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawLine2D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawQuad2D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawWireQuad2D		( const DebugRenderOptionsT* renderObject ) const;
	void							DrawDisc2D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawRing2D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawArrow2D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawText2D			( const DebugRenderOptionsT* renderObject ) const;

	//Draw methods 3D
	void							DrawPoint3D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawQuad3D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawLine3D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawSphere			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawWireSphere		( const DebugRenderOptionsT* renderObject )	const;
	void							DrawBox				( const DebugRenderOptionsT* renderObject ) const;
	void							DrawWireBox			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawText3D			( const DebugRenderOptionsT* renderObject ) const;
	void							DrawCapsule3D		(const DebugRenderOptionsT* renderObject) const;
	void							DrawWireCapsule3D	(const DebugRenderOptionsT* renderObject) const;

	//Destroy objects functions
	void							DestroyAllScreenObjects();
	void							DestroyAllWorldObjects();

private:
	const static Rgba						DEBUG_INFO;
	const static Rgba						DEBUG_ECHO;
	const static Rgba						DEBUG_BG_COLOR;

	const float								m_logFontHeight = DEFAULT_LOG_TEXT_HEIGHT;

	RenderContext*							m_renderContext					= nullptr;
	int										m_clientWidth					= 0;
	int										m_clientHeight					= 0;
	Camera*									m_debug3DCam					= nullptr;
	Camera*									m_debug2DCam					= nullptr;
	bool									m_canRender						= true;
	
	//2D world size
	Vec2									m_worldMin2D					= Vec2::ZERO;
	Vec2									m_worldMax2D					= Vec2::ZERO;

	BitmapFont*								m_debugFont						= nullptr;
	Shader*									m_xrayShader					= nullptr;
	Shader*									m_defaultShader					= nullptr;
	std::string								m_defaultShaderPath				= "default_unlit.xml";
	std::string								m_xmlShaderPath					= "default_unlit_xray.xml";

	//Keep a reference to the DebugRender instance for use with event systems
	static DebugRender*						s_debugRender;

	//Store all debug objects with their render options and other data
	std::vector<DebugRenderOptionsT>		m_worldRenderObjects;
	std::vector<DebugRenderOptionsT>		m_screenRenderObjects;

	std::vector<DebugRenderOptionsT>		m_printLogObjects;
};

This also needed some classes to define the debug object properties for the different shapes I intend to draw. To do so, I incorporated a DebugObjectProperties file which contained an ObjectProperties base class and all shapes inherited their properties from it.

The code for the DebugObjectProperties is highlighted below:

//------------------------------------------------------------------------------------------------------------------------------
enum eDebugRenderObject
{
	DEBUG_RENDER_POINT,
	DEBUG_RENDER_POINT3D,

	DEBUG_RENDER_LINE,
	DEBUG_RENDER_LINE3D,

	DEBUG_RENDER_QUAD,
	DEBUG_RENDER_WIRE_QUAD,
	DEBUG_RENDER_QUAD3D,

	DEBUG_RENDER_DISC,
	DEBUG_RENDER_RING,

	DEBUG_RENDER_SPHERE,
	DEBUG_RENDER_WIRE_SPHERE,	

	DEBUG_RENDER_BOX,
	DEBUG_RENDER_WIRE_BOX,

	DEBUG_RENDER_ARROW,
	DEBUG_RENDER_ARROW3D,

	DEBUG_RENDER_CAPSULE,
	DEBUG_RENDER_WIRE_CAPSULE,

	DEBUG_RENDER_BASIS,

	DEBUG_RENDER_TEXT,
	DEBUG_RENDER_TEXT3D,

	DEBUG_RENDER_LOG
};

//------------------------------------------------------------------------------------------------------------------------------
class ObjectProperties
{
public:
	virtual ~ObjectProperties();
	eDebugRenderObject m_renderObjectType;
	float m_durationSeconds         = 0.0f;  // show for a single frame
	float m_startDuration			= 0.f;
	Rgba m_currentColor				= Rgba::WHITE;

	CPUMesh* m_mesh;
};
//------------------------------------------------------------------------------------------------------------------------------
//	2D Render Objects
//------------------------------------------------------------------------------------------------------------------------------

// Point
//------------------------------------------------------------------------------------------------------------------------------
class Point2DProperties : public ObjectProperties
{
public:
	explicit Point2DProperties(eDebugRenderObject renderObject, const Vec2& screenPosition, 
							   float durationSeconds = 0.f, float size = DEFAULT_POINT_SIZE);
	virtual ~Point2DProperties();

public:
	Vec2 m_screenPosition				= Vec2::ZERO;
	float m_size						= DEFAULT_POINT_SIZE;
};

// Line
//------------------------------------------------------------------------------------------------------------------------------
class Line2DProperties : public ObjectProperties
{
public:
	explicit Line2DProperties(eDebugRenderObject renderObject, const Vec2& startPos, const Vec2& endPos, 
							  float durationSeconds = 0.f, float lineWidth = DEFAULT_LINE_WIDTH);
	virtual ~Line2DProperties();

public:
	Vec2 m_startPos						= Vec2::ZERO;
	Vec2 m_endPos						= Vec2::ZERO;
	float m_lineWidth					= DEFAULT_LINE_WIDTH;

};

// Arrow
//------------------------------------------------------------------------------------------------------------------------------
class Arrow2DProperties : public ObjectProperties
{
public:
	explicit Arrow2DProperties(eDebugRenderObject renderObject, const Vec2& start, const Vec2& end,
							   float durationSeconds = 0.f, float lineWidth = DEFAULT_LINE_WIDTH);
	virtual ~Arrow2DProperties();

public:
	Vec2 m_startPos						= Vec2::ZERO;
	Vec2 m_endPos						= Vec2::ZERO;
	float m_lineWidth					= DEFAULT_LINE_WIDTH;

	Vec2 m_lineEnd						= Vec2::ZERO;
	Vec2 m_arrowTip						= Vec2::ZERO;
	Vec2 m_lineNorm						= Vec2::ZERO;

};

// Quad
//------------------------------------------------------------------------------------------------------------------------------
class Quad2DProperties : public ObjectProperties
{
public:
	explicit Quad2DProperties( eDebugRenderObject renderObject, const AABB2& quad, 
							   float durationSeconds = 0.f, float thickness = DEFAULT_WIRE_WIDTH_2D, TextureView* texture = nullptr );
	virtual ~Quad2DProperties();

public:
	TextureView* m_texture				= nullptr;
	float m_thickness					= DEFAULT_WIRE_WIDTH_2D;	//Only used when rendering as a wire box
	AABB2	m_quad;
};

// Disc
//------------------------------------------------------------------------------------------------------------------------------
class Disc2DProperties : public ObjectProperties
{
public:
	explicit Disc2DProperties( eDebugRenderObject renderObject, const Disc2D& disc, float thickness, 
							   float durationSeconds = 0.f);
	virtual ~Disc2DProperties();

public:
	Disc2D m_disc;
	float m_thickness;		//Only used when rendering it as a ring
};
// Point
//------------------------------------------------------------------------------------------------------------------------------
class Point3DProperties : public ObjectProperties
{
public:
	explicit Point3DProperties( eDebugRenderObject renderObject, const Vec3& position, float size = DEFAULT_POINT_SIZE_3D, 
								float durationSeconds = 0.f, TextureView* texture = nullptr);
	virtual ~Point3DProperties();

public:
	Vec3 m_position						= Vec3::ZERO;
	TextureView* m_texture				= nullptr;
	float m_size						= DEFAULT_POINT_SIZE_3D;
	
	AABB2 m_point;
};

// Line
//------------------------------------------------------------------------------------------------------------------------------
class Line3DProperties : public ObjectProperties
{
public:
	explicit Line3DProperties( eDebugRenderObject renderObject, const Vec3& startPos, const Vec3& endPos, 
							   float durationSeconds = 0.f, float lineWidth = DEFAULT_LINE_WIDTH);
	virtual ~Line3DProperties();

public:
	Vec3 m_startPos						= Vec3::ZERO;
	Vec3 m_endPos						= Vec3::ZERO;
	Vec3 m_center						= Vec3::ZERO;
	float m_lineWidth					= DEFAULT_LINE_WIDTH;

	AABB2 m_line;
};

// Quad
//------------------------------------------------------------------------------------------------------------------------------
class Quad3DProperties : public ObjectProperties
{
public:
	explicit Quad3DProperties( eDebugRenderObject renderObject, const AABB2& quad, const Vec3& position, 
							   float durationSeconds = 0.f, TextureView* texture = nullptr, bool billBoarded = true );
	virtual ~Quad3DProperties();

public:
	Vec3 m_position						= Vec3::ZERO;
	TextureView* m_texture				= nullptr;
	bool m_billBoarded					= true;

	AABB2 m_quad;
};

// Sphere
//------------------------------------------------------------------------------------------------------------------------------
class SphereProperties : public ObjectProperties
{
public:
	explicit SphereProperties( eDebugRenderObject renderObject, const Vec3& center, float radius, 
							   float durationSeconds = 0.f, TextureView* texture = nullptr);
	virtual ~SphereProperties();

public:
	Vec3 m_center						= Vec3::ZERO;
	float m_radius						= 0.f;
	TextureView* m_texture				= nullptr;
};

// Capsule
//------------------------------------------------------------------------------------------------------------------------------
class CapsuleProperties : public ObjectProperties
{
public:
	explicit CapsuleProperties( eDebugRenderObject renderObject, const Capsule3D& capsule, const Vec3& position,
		float durationSeconds = 0.f, TextureView* texture = nullptr);
	virtual ~CapsuleProperties();

public:
	Vec3 m_position = Vec3::ZERO;
	TextureView* m_texture = nullptr;

	Capsule3D m_capsule;
};

// Box
//------------------------------------------------------------------------------------------------------------------------------
class BoxProperties : public ObjectProperties
{
public:
	explicit BoxProperties( eDebugRenderObject renderObject, const AABB3& box, const Vec3& position, 
							float durationSeconds = 0.f, TextureView* texture = nullptr);
	virtual ~BoxProperties();

public:
	Vec3 m_position						= Vec3::ZERO;
	TextureView* m_texture				= nullptr;

	AABB3 m_box;
};
//------------------------------------------------------------------------------------------------------------------------------
// Text Render Objects
//------------------------------------------------------------------------------------------------------------------------------
class TextProperties : public ObjectProperties
{
public:
	explicit TextProperties( eDebugRenderObject renderObject, const Vec3& position, const Vec2& pivot, const std::string& text, 
							 float fontHeight, float durationSeconds = 0.f, bool isBillboarded = true);
	explicit TextProperties( eDebugRenderObject renderObject, const Vec2& startPosition, const Vec2& endPosition, const std::string& text,
							 float fontHeight, float durationSeconds = 0.f);
	virtual ~TextProperties();

public:
	//For 3D
	Vec3 m_position							= Vec3::ZERO;
	Vec2 m_pivot							= Vec2::ZERO;
	bool m_isBillboarded					= true;

	//For 2D
	Vec2 m_startPosition					= Vec2::ZERO;
	Vec2 m_endPosition						= Vec2::ZERO;

	float m_fontHeight						= DEFAULT_TEXT_HEIGHT_3D;
	std::string	m_string;
};

//------------------------------------------------------------------------------------------------------------------------------
// Text Log Entry
//------------------------------------------------------------------------------------------------------------------------------
class LogProperties : public ObjectProperties
{
public:
	explicit LogProperties(eDebugRenderObject renderObject, const Rgba& printColor, const std::string& printString, float durationSeconds = 0.f);
	virtual ~LogProperties();

public:
	Rgba m_printColor						= Rgba::WHITE;

	std::string m_string;
};

With these property classes, the Debug Renderer is able to create the required shapes and populate their required properties. The only thing left to do now is to create the required objects to draw and queue them for a draw call.

Below is the logic for the DebugRenderWireSphere function

void DebugRender::DebugRenderWireSphere( DebugRenderOptionsT options, Vec3 center, float radius, float duration /*= 0.f*/, TextureView* texture /*= nullptr */ )
{
	options.objectProperties = new SphereProperties(DEBUG_RENDER_WIRE_SPHERE, center, radius, duration, texture);

	m_worldRenderObjects.push_back(options);
}

As can be seen, the function populates debug render options with a new objectProperty which in this case is a WireSphere

When the debug render function is called, the DebugRenderToCamera function executes all draw calls for 3D objects in the scene. This is what the DebugRenderToCamera function on DebugRenderer looks like:

void DebugRender::DebugRenderToCamera() const
{
	if(!m_canRender)
	{
		return;
	}

	//Use this method to render to the world camera
	int vectorSize = static_cast<int>(m_worldRenderObjects.size());

	for(int objectIndex = 0; objectIndex < vectorSize; objectIndex++)
	{
		const DebugRenderOptionsT* renderObject = &m_worldRenderObjects[objectIndex];
		switch(renderObject->objectProperties->m_renderObjectType)
		{
		case DEBUG_RENDER_POINT3D:		{	DrawPoint3D(renderObject);			}	break;
		case DEBUG_RENDER_LINE3D:		{	DrawLine3D(renderObject);			}	break;
		case DEBUG_RENDER_SPHERE:		{	DrawSphere(renderObject);			}	break;
		case DEBUG_RENDER_BOX:			{	DrawBox(renderObject);				}	break;
		case DEBUG_RENDER_QUAD3D:		{	DrawQuad3D(renderObject);			}	break;
		case DEBUG_RENDER_WIRE_SPHERE:	{	DrawWireSphere(renderObject);		}	break;
		case DEBUG_RENDER_WIRE_BOX:		{	DrawWireBox(renderObject);			}	break;
		case DEBUG_RENDER_TEXT3D:		{	DrawText3D(renderObject);			}	break;
		case DEBUG_RENDER_CAPSULE:		{	DrawCapsule3D(renderObject);		}	break;
		case DEBUG_RENDER_WIRE_CAPSULE:	{	DrawWireCapsule3D(renderObject);	}	break;
		default:						{	ERROR_AND_DIE("The debug object is not yet defined in DebugRenderToCamera");	}	break;
		}
	}
}

Taking a look at the code for DrawWireSphere, the implementation followed is highlighted below:

void DebugRender::DrawWireSphere( const DebugRenderOptionsT* renderObject ) const
{
	SphereProperties* objectProperties = reinterpret_cast<SphereProperties*>(renderObject->objectProperties);
	if(objectProperties->m_renderObjectType != DEBUG_RENDER_WIRE_SPHERE)
	{
		ERROR_AND_DIE("Object recieved in DebugRender was not a Sphere. Check inputs");
	}

	//Setup mesh here
	GPUMesh sphere = GPUMesh( m_renderContext ); 
	sphere.CreateFromCPUMesh<Vertex_PCU>( objectProperties->m_mesh, GPU_MEMORY_USAGE_STATIC );
	SetObjectMatrixForPosition(objectProperties->m_center);

	//Setup the textures on the render context
	m_renderContext->BindTextureViewWithSampler(0U, objectProperties->m_texture); 

	m_renderContext->SetRasterStateWireFrame();
	switch (renderObject->mode)
	{
		case DEBUG_RENDER_USE_DEPTH:
		m_renderContext->SetDepth(true);
		m_renderContext->DrawMesh(&sphere);
		break;
		case DEBUG_RENDER_ALWAYS:
		m_renderContext->SetDepth(false);
		m_renderContext->DrawMesh(&sphere);
		break;
		case DEBUG_RENDER_XRAY:
		{
			//Make 2 draw calls here
			//One with compare op lequals and one with compare op greater than (edit alpha on that one)
			m_renderContext->SetGlobalTint(Rgba::DARK_GREY);
			m_xrayShader->SetDepth(eCompareOp::COMPARE_GREATER, false);
			m_renderContext->BindShader(m_xrayShader);
			m_renderContext->DrawMesh(&sphere);
			m_renderContext->BindShader(m_defaultShader);
			m_renderContext->DrawMesh(&sphere);
		}
		break;
	}
	m_renderContext->CreateAndSetDefaultRasterState();
}

Similar to the case of the WireSphere, all other debug shapes follow a similar queue and draw paradigm.

Here’s a view with only the 3D objects being rendered:

Only 3D debug objects rendered

Considerations Made

  • This was a debug system so performance wasn’t as important
  • I want something easy to use
  • Should be able to support all primitive shapes
  • Should be able to use it’s own debug cameras to render
  • 2D and 3D should be completely different draw calls and draw logic

Retrospectives

What went well 🙂

  • Implemented the system to be simple for end-user
  • Support for all primitive shapes in 2D and 3D
  • Support for text in 2D and 3D

What went wrong 🙁

  • Still pretty over-engineered in my opinion
  • Couldn’t get arrows in 3D
  • No ico-sphere support

What I would do differently 🙂

  • Simplify the system to not have to create multiple objects
  • Re-use shapes by keeping some static CPU Meshes
  • Implement 3D arrows and a basis vector