Box2D DebugDraw (b2Draw implementation) for WINAPI, WINGDI
Quite often I create simple or test apps using native Win32 project (WINAPI). This time I wanted an output of Box2D world. No ready-to-copypaste implementation was found in the Net, so I had to make my own, and now I can share it
And here is the source code:
Header file
// DebugDrawGDI.h
//
//
// Created by PavlAl on 06.05.2013
// Copyright (c) 2013 tatalata, http://talatala.com
//
#include <Box2D/Box2D.h>
#pragma once
class DebugDrawGDI : public b2Draw
{
public:
DebugDrawGDI();
~DebugDrawGDI();
/// Initialize drawing of a whole world.
void Begin(RECT & winRect, HDC hdc, b2World *world);
/// Initialize drwaing in a certain AABB.
void Begin(RECT & winRect, HDC hdc, b2World *world, const b2AABB & aabb);
/// Unintialize drawing.
void End();
/// Draw a closed polygon provided in CCW order.
virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
/// Draw a solid closed polygon provided in CCW order.
virtual void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
/// Draw a circle.
virtual void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color);
/// Draw a solid circle.
virtual void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color);
/// Draw a line segment.
virtual void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color);
/// Draw a transform. Choose your own length scale.
/// @param xf a transform.
virtual void DrawTransform(const b2Transform& xf);
private:
/// Setup current brush and pen.
void SetColor(const b2Color& color);
/// Delete current brush and pen.
void CleanupGDIObjects();
private:
/// Reference to a HDC set in Begin method.
HDC m_hdc;
/// Last used brush, owned by DebugDrawGDI.
HBRUSH m_brush;
/// Last used pen, DebugDrawGDI.
HPEN m_pen;
/// Last used color.
DWORD m_brushColor;
};
Implementation file
// DebugDrawGDI.cpp
// RogueRunner
//
// Created by PavlAl on 06.05.2013
// Copyright (c) 2013 tatalata, http://talatala.com
//
#include "stdafx.h"
#include "DebugDrawGDI.h"
#include <vector>
#define DEBUG_DRAW_MAX_WORLD_SIZE 1000
#define SCALE 1000.f
#define UP(v) ((v) * SCALE)
#define UPi(v) ((int)UP(v))
class AABBDetector : public b2QueryCallback
{
public:
b2AABB aabb;
AABBDetector()
{
aabb.lowerBound = b2Vec2(DEBUG_DRAW_MAX_WORLD_SIZE, DEBUG_DRAW_MAX_WORLD_SIZE);
aabb.upperBound = b2Vec2(-DEBUG_DRAW_MAX_WORLD_SIZE, -DEBUG_DRAW_MAX_WORLD_SIZE);
}
bool ReportFixture(b2Fixture* fixture)
{
b2Vec2 pos = fixture->GetBody()->GetPosition();
if (aabb.lowerBound.x > pos.x) aabb.lowerBound.x = pos.x;
if (aabb.upperBound.x < pos.x) aabb.upperBound.x = pos.x;
if (aabb.lowerBound.y > pos.y) aabb.lowerBound.y = pos.y;
if (aabb.upperBound.y < pos.y) aabb.upperBound.y = pos.y;
return true;
}
};
namespace
{
b2AABB CreateAABB(float xMin, float yMin, float xMax, float yMax)
{
b2AABB aabb;
aabb.lowerBound = b2Vec2(xMin, yMin);
aabb.upperBound = b2Vec2(xMax, yMax);
return aabb;
}
}
DebugDrawGDI::DebugDrawGDI()
: m_hdc(NULL)
, m_brush(NULL)
, m_pen(NULL)
, m_brushColor(0)
{
}
DebugDrawGDI::~DebugDrawGDI()
{
CleanupGDIObjects();
}
void DebugDrawGDI::Begin(RECT & winRect, HDC hdc, b2World *world, const b2AABB & aabb)
{
m_hdc = hdc;
world->SetDebugDraw(this);
BOOL bOK = SetGraphicsMode(hdc, GM_ADVANCED);
ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
// Cleanup area.
FillRect(hdc, &winRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
// Change world transform.
int outputWidth = winRect.right - winRect.left;
int outputHeight = winRect.bottom - winRect.top;
float boundsWidth = UP(aabb.upperBound.x - aabb.lowerBound.x);
float boundsHeight = UP(aabb.upperBound.y - aabb.lowerBound.y);
float cx = outputWidth / boundsWidth;
float cy = outputHeight / boundsHeight;
float c = cx > cy ? cy : cx;
XFORM x;
x.eM11 = c;
x.eM21 = 0;
x.eDx = winRect.left + (outputWidth - boundsWidth * c) / 2 - UP(aabb.lowerBound.x) * c;
x.eM12 = 0;
x.eM22 = -c;
x.eDy = winRect.top + (outputHeight - boundsHeight * c) / 2 + UP(aabb.upperBound.y) * c;
SetWorldTransform(hdc, &x);
}
void DebugDrawGDI::Begin(RECT & winRect, HDC hdc, b2World *world)
{
// Get world AABB.
AABBDetector d;
world->QueryAABB(&d, CreateAABB(-DEBUG_DRAW_MAX_WORLD_SIZE, -DEBUG_DRAW_MAX_WORLD_SIZE, DEBUG_DRAW_MAX_WORLD_SIZE, DEBUG_DRAW_MAX_WORLD_SIZE));
d.aabb.lowerBound -= b2Vec2(1, 1);
d.aabb.upperBound += b2Vec2(1, 1);
Begin(winRect, hdc, world, d.aabb);
}
void DebugDrawGDI::End()
{
ModifyWorldTransform(m_hdc, NULL, MWT_IDENTITY);
m_hdc = NULL;
}
void DebugDrawGDI::CleanupGDIObjects()
{
if (m_brush) ::DeleteObject(m_brush);
if (m_pen) ::DeleteObject(m_pen);
m_brush = NULL;
m_pen = NULL;
}
void DebugDrawGDI::SetColor(const b2Color& color)
{
DWORD rgb = RGB(color.r * 255, color.g*255, color.b*255);
if (!m_brush || !m_pen || m_brushColor != rgb)
{
CleanupGDIObjects();
m_brush = ::CreateSolidBrush(rgb);
m_pen = ::CreatePen(PS_SOLID, 1, RGB(color.r * 155, color.g*155, color.b*155));
m_brushColor = rgb;
}
::SelectObject(m_hdc, m_brush);
::SelectObject(m_hdc, m_pen);
}
/// Draw a closed polygon provided in CCW order.
void DebugDrawGDI::DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
{
std::vector<POINT> pts;
pts.reserve(vertexCount + 1);
for (int i = 0; i < vertexCount; ++i, ++vertices)
{
POINT p;
p.x = UPi(vertices->x);
p.y = UPi(vertices->y);
pts.push_back(p);
}
pts.push_back(pts.front());
SetColor(color);
::Polyline(m_hdc, &pts[0], vertexCount + 1);
}
/// Draw a solid closed polygon provided in CCW order.
void DebugDrawGDI::DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
{
std::vector<POINT> pts;
pts.reserve(vertexCount + 1);
for (int i = 0; i < vertexCount; ++i, ++vertices)
{
POINT p;
p.x = UPi(vertices->x);
p.y = UPi(vertices->y);
pts.push_back(p);
}
pts.push_back(pts.front());
SetColor(color);
::Polygon(m_hdc, &pts[0], vertexCount + 1);
}
/// Draw a circle.
void DebugDrawGDI::DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color)
{
SetColor(color);
// Cleanup fill brush.
::SelectObject(m_hdc, GetStockObject(NULL_BRUSH));
::Ellipse(
m_hdc,
UPi(center.x - radius),
UPi(center.y - radius),
UPi(center.x + radius),
UPi(center.y + radius));
SetColor(color);
}
/// Draw a solid circle.
void DebugDrawGDI::DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color)
{
SetColor(color);
::Ellipse(
m_hdc,
UPi(center.x - radius),
UPi(center.y - radius),
UPi(center.x + radius),
UPi(center.y + radius));
DrawSegment(center, center + b2Vec2(axis.x * radius, axis.y *radius), color);
}
/// Draw a line segment.
void DebugDrawGDI::DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color)
{
SetColor(color);
::MoveToEx(m_hdc, UPi(p1.x), UPi(p1.y), NULL);
::LineTo(m_hdc, UPi(p2.x), UPi(p2.y));
}
/// Draw a transform. Choose your own length scale.
/// @param xf a transform.
void DebugDrawGDI::DrawTransform(const b2Transform& xf)
{
const float length = 1;
b2Vec2 start = xf.p;
b2Vec2 axis = xf.q.GetXAxis();
b2Vec2 end = start + b2Vec2(axis.x * length, axis.y * length);
::MoveToEx(m_hdc, UPi(start.x), UPi(start.y), NULL);
::LineTo(m_hdc, UPi(end.x), UPi(end.y));
}





