Thursday, March 1

Isometric Projection Matrix

One thing that seems to get asked a lot is how to create an isometric style game using a 3d pipline and textured quads, instead of 2d sprite tiles. It is also a question that does not seem to get answered much, so I thought I would whip out a quick demonstration of how to do it with the Irrlicht Engine.


One thing to note is that this is not a textbook isometric projection which is a form of axonometric projection, but a fudge that makes the width and height of a cube in the viewport equal. This emulates tile based games nicely. Of course, with a "real" projection it might be possible to dynamically change the projection angle, which might be an interesting effect.




#include <irrlicht.h>

using namespace irr;

using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;



int main()
{


IrrlichtDevice *device =
createDevice( video::EDT_SOFTWARE2, dimension2d<s32>(640, 480), 16,
false, false, false, 0);

device->setWindowCaption(L"Isometric Projection");

IVideoDriver* driver = device->getVideoDriver();


while(device->run())
{
/*
Anything can be drawn between a beginScene() and an endScene()
call. The beginScene clears the screen with a color and also
the depth buffer if wanted. Then we let the Scene Manager and
the GUI Environment draw their content. With the endScene()
call everything is presented on the screen.
*/

driver->beginScene(true, true, SColor(255,100,101,140));

/**
* we are not interested in this transform so let us set it to
* identity
*/

driver->setTransform(video::ETS_WORLD, core::matrix4());
driver->setTransform(video::ETS_VIEW, core::matrix4());

/**
* first portion of transform - in matrix form
* x' = (x - z)
* y' = y + 0.5 * ( x + z )
* this maps the z to the x and y axes in such a way that
* the result appears isometric.
*/

matrix4 projMatrix;
projMatrix.makeIdentity();
projMatrix.M[0] = 1.0f;
projMatrix.M[8] = -1.0f;
projMatrix.M[1] = 0.5f;
projMatrix.M[5] = 1.0f;
projMatrix.M[9] = 0.5f;
projMatrix.M[10] = 0.0;

/**
* second portion of transform -- scale to fit clipping
* volume. If the scale is 1.0f then the unit vectors fit the
* clipping volume. The volume is a cuboid, centered in the origin.
* The scaling will determine the size of this volume which will
* contain the portion of the world that we can see.
*/

f32 scale = 4.0f;
matrix4 clipMatrix;
clipMatrix.buildProjectionMatrixOrthoLH(2 * scale, 2 * scale ,-1 * scale, 2 * scale);


/**
* concatentate transform - we multiply transforms together in
* the opposite order to that which we would apply them to a
* vector because of the law of associativity applies to
* matrices, but not commutivity and NM != MN fun,
* eh..matrices..gotta luv em..
*/

projMatrix = clipMatrix * projMatrix;

/**
* ok we now have our projection matrix
*/

driver->setTransform(video::ETS_PROJECTION, projMatrix);


/**
* draw unit x, y, and z vectors in our new space
*/


// x axis is red
driver->draw3DLine(vector3df(-1.0, 0.0, 0.0),
vector3df(1.0, 0.0, 0.0),
SColor(255,255,0,0));

// y axis is green
driver->draw3DLine(vector3df(0.0, -1.0, 0.0),
vector3df(0.0, 1.0, 0.0),
SColor(255,0,255,0));

// z axis is blue
driver->draw3DLine(vector3df(0.0, 0.0, -1.0),
vector3df(0.0, 0.0, 1.0),
SColor(255,0,0,255));

/**
* done
*/

driver->endScene();
}

device->drop();

return 0;
}

1 comment:

Scippie said...

Thank you VERY MUCH!
I have been searching for this for a long time.

I don't know if it is correct, but it is perfect for a game!