Lexa

WPF, C#, Objective C and a little Math

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));
}

The Last Mission v 1.2 released

Wow! A new release is finally available in the AppStore: The Last Mission 1.2

It includes following features:

- 4 new playable ships with different weapons.
- Health bonus in easy level.
- Improved graphics: dynamic lights and details on level backgrounds.
- New music themes and sound effects created by Mark Braga specially for The Last Mission.
- Brand new secret level with bonus ship at the end.
- Integration with the Game Center (worldwide leader board).
- Game status is saved between app runs.
- Updated GUI with a little help on the game.
- Adjusted controls positions for iPad, iCade support (bluetooth joystics).

Now game is finally playable and fun, I am very happy with results and can continue with my other project :)

Meanwhile, the author of the remake, Dmitry Smagin is planning to continue development and release the new version for Windows and open-source portable systems. I’m looking forward for it.


Top secret!

The Last Mission new release is on the way, it will have new sound, updated graphics, new ships, and a secret level  (it will be possible to play for a “normal” robot!):

 


Cisco VPN client connection error using Speedstick LTE Modem (Huawei)

I had a problem using Cisco VPN connection when I was online via USB LTE modem (that was Huawei): no access to mapped drives was possible, no Outlook connection as well. I had not found any solution in the Internet, and my system administrator solved the problem only after discussion with provider customer service.

Last week, in ICE train I met guys discussing the very same issue, so it seems quite a useful hint. Here it is:
1. Open the folder with software installed from stick (in my case it was: C:\Program Files (x86)\T-Mobile\InternetManager_H\)
2. Locate there a file named SysSetting.xml and edit it
3. Find the following part in the file: <Connect> <type>NDIS</type> and replace NDIS with DUN
4. Save the file and restart the software.

I used the same trick with T-Mobile an O2 sticks as well, worked fine. I wish you a joyful mobile surfing in corporate networks :)


The Last Mission Remake for iPhone and iPad

Last week „The Last Mission“ app was published in the AppStore.

It all started from the book called „Learn cocos2d 2“ (here is the link to the authors amazon page). Very well written book with a lot of examples and hints.

Then I got an idea of the project: The Last Mission remake (so far I have not seen any game using the same game concept in the app store). After 5 minutes of browsing I found that the game is already re-implemented and it is also available with source code, here is the link to the project of Dmitry Smagin: https://github.com/dmitrysmagin/last-mission

Still, it was not ported for the iOS, so I decided to give it a try. In a day my port was completed with almost no changes of the original source code. The usage of cocos was not so havy as I wanted: every 17 milliseconds the original game engine generated an image, and the app updates the texture of the „game sprite“. Quite boring, almost nothing to implement, but it was playable and looked fun.

So I dropped a mail to Dmitry and just in few hours I had his permission to publish the game for the iOS. That was just great!

Only 2 small things left: controls and the sound.

  • For controls I used the SneakyInput library. I had to modify the sources of the Joystick, because default SneakyInput D-Pad was more like a Joystick with limited number of positions, rather than a button which can be pressed from few directions. Anyway, the result is quite OK on my opinion, the D-Pad is quite intuitive and easy to use.
  • The original sound implementation was not adopted to the iOS (shame on me), I just tooks some mp3s and the default cocos2d sound library did the rest of the job.

So, in a week the app was completed and submitted to the store.

And here are the sources, if you want to build a game on your own: Mission.zip

Thanks for reading!