the first thing we need for drawing 2d graphic is a good transformation tool. the matrix2D class has the complete 2D operation needed to transform vertices in 2d manner. it has identity, scale, rotate, translate, invert, and concat operation etc.
- identity reset the matrix to identity
- scale -> scale the matrix
- rotate -> rotate the matrix
- translate -> translate the matrix
- invert -> invert the matrix, basically do the oposite of the previous transformation
- concat -> basically matrix multiplication, or in other word, combine 2 or more operation
- matrix->translate(-25,-25); // translate to negative half of the square's edge. because we want it to perfectly centered
- matrix->translate(400,300); // translate to the midle of the screen
- transform the 4 vertices
- matrix->translate(400 - 25, 300 -25)
- and transform the vertices
Code: Select all
/** \brief 2D matrix math, handles 2d transformation, based on as3 adobe flash matrix. created by Kornelius Heru Cakra Murti 2016
*/
class matrix2D
{
public:
/// x scale
f32 a;
/// y skewness
f32 b;
/// x skewness
f32 c;
/// y scale
f32 d;
/// translation x
f32 tx;
/// translation y
f32 ty;
/** \brief construct a matrix object
*/
matrix2D()
{
a = 1.0;
b = 0.0;
c = 0.0;
d = 1.0;
tx = 0.0;
ty = 0.0;
}
/** \brief destruct a matrix object
*/
~matrix2D();
/** \brief reset matrix, change value to identity
* \return void.
*/
void identity()
{
a = 1.0;
b = 0.0;
c = 0.0;
d = 1.0;
tx = 0.0;
ty = 0.0;
}
/** \brief concatenate matrix with other matrix.
* \param other matrix to concatenate to.
* \return void.
*/
void concat(const matrix2D * other)
{
f32 a1 = a;
f32 b1 = b;
f32 c1 = c;
f32 d1 = d;
f32 tx1 = tx;
f32 ty1 = ty;
f32 a2 = other->a;
f32 b2 = other->b;
f32 c2 = other->c;
f32 d2 = other->d;
f32 tx2 = other->tx;
f32 ty2 = other->ty;
a = a1 * a2 + b1 * c2;
b = a1 * b2 + b1 * d2;
c = c1 * a2 + d1 * c2;
d = c1 * b2 + d1 * d2;
tx = tx1 * a2 + ty1 * c2 + tx2;
ty = tx1 * b2 + ty1 * d2 + ty2;
}
/** \brief invert matrix. inversed matrix does the opposite transformation
* \return void.
*/
void invert()
{
f32 ta = a;
f32 tb = b;
f32 tc = c;
f32 td = d;
f32 ttx = tx;
f32 tty = ty;
f32 tadbc = ta * td - tb * tc;
a = td / tadbc;
b = -tb / tadbc;
c = -tc / tadbc;
d = ta / tadbc;
tx = ( tc * tty - td * ttx ) / tadbc;
ty = -( ta * tty - tb * ttx ) / tadbc;
}
/** \brief translate the matrix object.
* \param x how much translation in x direction.
* \param y how much translation in y direction.
* \return void.
*/
void translate(const f32 x, const f32 y)
{
tx += x;
ty += y;
}
/** \brief scale the matrix object.
* \param x how much scaling in x direction.
* \param y how much scaling in y direction.
* \return void.
*/
void scale(const f32 x, const f32 y)
{
a *= x;
b *= y;
c *= x;
d *= y;
tx *= x;
ty *= y;
}
/** \brief rotate the matrix object.
* \param rad how much rotation in radians
* \return void.
*/
void rotate(const f32 rad)
{
f32 ta = a;
f32 tb = b;
f32 tc = c;
f32 td = d;
f32 ttx = tx;
f32 tty = ty;
f32 tcos = cos(rad);
f32 tsin = sin(rad);
a = ta * tcos - tb * tsin;
b = ta * tsin + tb * tcos;
c = tc * tcos - td * tsin;
d = tc * tsin + td * tcos;
tx = ttx * tcos - tty * tsin;
ty = ttx * tsin + tty * tcos;
}
/** \brief copy other matrix values.
* \param other other matrix to copy from.
* \return void.
*/
void copyFrom(const matrix2D* other)
{
a = other->a;
b = other->b;
c = other->c;
d = other->d;
tx = other->tx;
ty = other->ty;
}
/** \brief transform a x y coordinate.
* \param x value to modify.
* \param y value to modify.
* \return void.
*/
void transformXY(f32 &x, f32 &y)
{
f32 rx = x;
f32 ry = y;
x = rx * a + ry * c + tx;
y = rx * b + ry * d + ty;
}
};
Code: Select all
class simpleObject
{
// hurrah all public :D :D
public:
ITexture* texture; // texture being use
f32 x; // x position in screen space
f32 y; // y position in screen space
f32 scaleX; // object scale
f32 scaleY; // object scale
f32 centerX; // center of the object. act like a pivot point for rotation etc
f32 centerY; // center of the object. act like a pivot point for rotation etc
f32 rotation; // rotation in radians
f32 width; // object width
f32 height; // object height
matrix2D* transformation; // object transformation matrix
matrix2D* deviceSpaceTransformation; // transformation to bring x y values to device space
S3DVertex vertices[4]; // 4 vertices for a quad
u16 indices[6]; // 6 indices for 2 triangle
SMaterial mat;
// simple constructor
simpleObject(ITexture* texture, f32 x, f32 y, f32 width, f32 height)
{
this->width = width;
this->height = height;
this->x = x;
this->y = y;
this->texture = texture;
centerX = 0;
centerY = 0;
scaleX = 1;
scaleY = 1;
rotation = 0;
transformation = new matrix2D();
deviceSpaceTransformation = new matrix2D();
/// initiate vertices, just basic vertex type for now.
vertices[0] = video::S3DVertex(0.0f,0.0f,0.0f,0,0,1,video::SColor(255,255,255,255), 0, 0);
vertices[1] = video::S3DVertex(0.0f,0.0f,0.0f,0,0,1,video::SColor(255,255,255,255),1.0, 0);
vertices[2] = video::S3DVertex(0.0f,0.0f,0.0f,0,0,1,video::SColor(255,255,255,255),1.0,1.0);
vertices[3] = video::S3DVertex(0.0f,0.0f,0.0f,0,0,1,video::SColor(255,255,255,255),0.0,1.0);
// initiate indices
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 2;
indices[4] = 3;
indices[5] = 0;
// setting up material
mat.setTexture(0, texture);
mat.setFlag(EMF_BACK_FACE_CULLING, false); // afraid of negative scale
mat.setFlag(EMF_LIGHTING, false); // no lighting
mat.MaterialType = EMT_SOLID; // best way to blend transparent texture
mat.BlendFactor = pack_textureBlendFunc(EBF_ONE, EBF_ONE_MINUS_SRC_ALPHA); // best way to blend transparent texture
mat.BlendOperation = EBO_ADD; // best way to blend transparent texture
mat.setFlag(EMF_ZWRITE_ENABLE, false); // no z operation
mat.setFlag(EMF_ZBUFFER, false); // no z operation
}
// finish this one your self :D
~simpleObject() {}
// update routine
void update()
{
// routine update
// can do a lot of stuff here. key frame animation? sprite sheet animation? :D
// for now just go vanilla and calculate transformation
transformation->identity();
transformation->translate( - centerX, - centerY);
transformation->scale(scaleX, scaleY);
transformation->rotate(rotation);
transformation->translate(x,y);
}
// method for drawing this object
void render(IVideoDriver* driver, matrix2D* deviceSpaceMatrix)
{
// concatenate the 2 important matrices;
deviceSpaceTransformation->identity();
deviceSpaceTransformation->copyFrom(transformation);
deviceSpaceTransformation->concat(deviceSpaceMatrix);
// update vertices position
// by transforming all 4 vertices
f32 x1 = 0;
f32 y1 = 0;
f32 x2 = width;
f32 y2 = 0;
f32 x3 = width;
f32 y3 = height;
f32 x4 = 0;
f32 y4 = height;
deviceSpaceTransformation->transformXY(x1,y1);
deviceSpaceTransformation->transformXY(x2,y2);
deviceSpaceTransformation->transformXY(x3,y3);
deviceSpaceTransformation->transformXY(x4,y4);
vertices[0].Pos.X = x1;
vertices[0].Pos.Y = y1;
vertices[1].Pos.X = x2;
vertices[1].Pos.Y = y2;
vertices[2].Pos.X = x3;
vertices[2].Pos.Y = y3;
vertices[3].Pos.X = x4;
vertices[3].Pos.Y = y4;
// all done.
// lets draw our object
driver->setMaterial(mat);
driver->drawIndexedTriangleList(&vertices[0], 4, &indices[0], 2);
}
// get pixel color from screen coordinate
SColor getColorFromScreenXY(f32 screenX, f32 screenY)
{
// invert transformation
transformation->invert();
// transform our screen position
f32 ptx = screenX;
f32 pty = screenY;
transformation->transformXY(ptx, pty);
// no negative
if((ptx >= 0)&&(pty >= 0))
{
// get our pixel
if(texture)
{
core::dimension2d<u32> dimension = texture->getOriginalSize();
u32 ppx = (u32)round(ptx);
u32 ppy = (u32)round(pty);
// no out of bound
if((ppx < dimension.Width) && (ppy < dimension.Height))
{
u8 * texels = (u8 *)texture->lock(ETLM_READ_WRITE,0);
s32 pitch = texture->getPitch();
ECOLOR_FORMAT format = texture->getColorFormat();
s32 bytes = video::IImage::getBitsPerPixelFromFormat(format) / 8;
SColor texel = *(u32*)(texels + ((ppy * pitch) + (ppx * bytes)));
texture->unlock();
return texel;
}
}
}
return SColor(255,0,0,0);
}
};
Code: Select all
// update routine
void update()
{
// routine update
// can do a lot of stuff here. key frame animation? sprite sheet animation? or sync up with rigid body transformation. :D
// for now just go vanilla and calculate transformation
transformation->identity(); // reset the matrix first
transformation->translate( - centerX, - centerY); // a nice pivot point for rotation and stuff
transformation->scale(scaleX, scaleY); // apply scale
transformation->rotate(rotation); // apply rotation
transformation->translate(x,y); // finally apply translation
}
Code: Select all
// calculate our device space matrix
deviceSpaceMatrix->identity();
deviceSpaceMatrix->scale( 1.0f / 800.0f * 2.0, 1.0 / 600.0f * 2.0); // asuming screen are 800x600. otherwise just change the 800x600 with any resolution.
deviceSpaceMatrix->translate( -1.0, -1.0);
deviceSpaceMatrix->scale( 1.0, -1.0);// flip y axis so we dont need to flip our thought.
Code: Select all
// method for drawing this object
void render(IVideoDriver* driver, matrix2D* deviceSpaceMatrix)
{
// concatenate the 2 important matrices;
deviceSpaceTransformation->identity();
deviceSpaceTransformation->copyFrom(transformation);
deviceSpaceTransformation->concat(deviceSpaceMatrix);
// update vertices position
// by transforming all 4 vertices
f32 x1 = 0;
f32 y1 = 0;
f32 x2 = width;
f32 y2 = 0;
f32 x3 = width;
f32 y3 = height;
f32 x4 = 0;
f32 y4 = height;
deviceSpaceTransformation->transformXY(x1,y1);
deviceSpaceTransformation->transformXY(x2,y2);
deviceSpaceTransformation->transformXY(x3,y3);
deviceSpaceTransformation->transformXY(x4,y4);
vertices[0].Pos.X = x1;
vertices[0].Pos.Y = y1;
vertices[1].Pos.X = x2;
vertices[1].Pos.Y = y2;
vertices[2].Pos.X = x3;
vertices[2].Pos.Y = y3;
vertices[3].Pos.X = x4;
vertices[3].Pos.Y = y4;
// all done.
// lets draw our object
driver->setMaterial(mat);
driver->drawIndexedTriangleList(&vertices[0], 4, &indices[0], 2);
}
Code: Select all
// reset transform for 3d rendering
ICameraSceneNode* activeCam = smgr->getActiveCamera();
if(activeCam)
{
driver->setTransform(video::ETS_VIEW, activeCam->getViewMatrix());
driver->setTransform(video::ETS_PROJECTION, activeCam->getProjectionMatrix());
}
smgr->drawAll();
guienv->drawAll();
// reset transform form 2d rendering
device->getVideoDriver()->setTransform(ETS_VIEW, matrix4());
device->getVideoDriver()->setTransform(ETS_PROJECTION, matrix4());
device->getVideoDriver()->setTransform(ETS_WORLD, matrix4());
Code: Select all
// setting up material
mat.setTexture(0, texture);
mat.setFlag(EMF_BACK_FACE_CULLING, false); // afraid of negative scale
mat.setFlag(EMF_LIGHTING, false); // no lighting
mat.MaterialType = EMT_SOLID; // best way to blend transparent texture
mat.BlendFactor = pack_textureBlendFunc(EBF_ONE, EBF_ONE_MINUS_SRC_ALPHA); // best way to blend transparent texture
mat.BlendOperation = EBO_ADD; // best way to blend transparent texture
mat.setFlag(EMF_ZWRITE_ENABLE, false); // no z operation
mat.setFlag(EMF_ZBUFFER, false); // no z operation
Code: Select all
int main(int argc, char** argv)
{
IrrlichtDevice *device =
createDevice(EDT_OPENGL, dimension2d<u32>(800, 600), 32,
false, false, false, 0);
device->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* guienv = device->getGUIEnvironment();
guienv->addStaticText(L"Hello World! This is the Irrlicht Software renderer!",
rect<int>(10,10,200,22), true);
// create the matrix
matrix2D* deviceSpaceMatrix = new matrix2D();
// create our objects
driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false); // 2d no need for mip maps
irr::core::array<simpleObject*> objects;
simpleObject* object1 = new simpleObject(driver->getTexture("./bambu.png"), 50,50,100,100);
simpleObject* object2 = new simpleObject(driver->getTexture("./kerokot.png"), 0,0,200,400);
object2->centerX = 100;
object2->centerY = 50;
object2->x = 400;
object2->y = 300;
objects.push_back(object1);
objects.push_back(object2);
f32 rotation = 0;
SColor textureColorFromMouse(255,255,255,255);
//search_me
// this is the texture lock before beginScene() & endScene()
textureColorFromMouse = object1->getColorFromScreenXY( 5, 5);
while(device->run())
{
driver->beginScene(true, true, SColor(0,200,200,200));
// reset transform for 3d rendering
ICameraSceneNode* activeCam = smgr->getActiveCamera();
if(activeCam)
{
driver->setTransform(video::ETS_VIEW, activeCam->getViewMatrix());
driver->setTransform(video::ETS_PROJECTION, activeCam->getProjectionMatrix());
}
smgr->drawAll();
guienv->drawAll();
// reset transform form 2d rendering
device->getVideoDriver()->setTransform(ETS_VIEW, matrix4());
device->getVideoDriver()->setTransform(ETS_PROJECTION, matrix4());
device->getVideoDriver()->setTransform(ETS_WORLD, matrix4());
// calculate our device space matrix
deviceSpaceMatrix->identity();
deviceSpaceMatrix->scale( 1.0f / 800.0f * 2.0, 1.0 / 600.0f * 2.0);
deviceSpaceMatrix->translate( -1.0, -1.0);
deviceSpaceMatrix->scale( 1.0, -1.0);
rotation += 0.01f;
object2->rotation = rotation;
// update and render all object
for(u8 i= 0 ; i < objects.size() ; i++)
{
objects[i]->update();
objects[i]->render(driver, deviceSpaceMatrix);
}
// test get objects pixel
position2d<s32> p = device->getCursorControl()->getPosition();
//search_me
// this is the textureLock inside beginScene() endScene()
textureColorFromMouse = object2->getColorFromScreenXY( p.X, p.Y);
driver->draw2DRectangle(SColor(255,0,0,0), rect<s32>(0,500,100, 600));
driver->draw2DRectangle(textureColorFromMouse, rect<s32>(5,505,95, 595));
driver->endScene();
device->sleep(30);
}
device->drop();
return 0;
}